From 6ee6031a5f65b9c2022ded7a64d5d4661cccf405 Mon Sep 17 00:00:00 2001 From: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:56:13 -0400 Subject: [PATCH 01/86] CapabilityStatement doe not declare conformance to IG when a bulk data export provider is registered. (#5329) * adding initial test * adding initial test * solution implementation * spotless apply * bumping to 6.9.8-SNAPSHOT * addressing comments from review and fixing typo * spotless --------- Co-authored-by: peartree --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../java/ca/uhn/fhir/rest/api/Constants.java | 3 + hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- ...nt-needs-to-declare-conformance-to-ig.yaml | 6 ++ hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- .../uhn/fhir/jpa/model/util/JpaConstants.java | 9 +-- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- .../jpa/bulk/BulkDataExportProviderTest.java | 63 +++++++-------- .../uhn/fhir/jpa/bulk/BulkDataExportTest.java | 5 +- .../fhir/jpa/bulk/BulkExportUseCaseTest.java | 7 +- .../provider/r4/BulkExportProviderR4Test.java | 5 +- .../provider/r4/MultitenantServerR4Test.java | 3 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../server/provider/ProviderConstants.java | 9 +++ .../ServerCapabilityStatementProvider.java | 15 ++++ .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../jobs/export/BulkDataExportProvider.java | 38 +++++++--- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- .../ServerCapabilityStatementProvider.java | 57 ++++++++++++-- ...rCapabilityStatementProviderDstu3Test.java | 22 ++++++ hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- .../provider/BulkDataExportProvider.java | 76 +++++++++++++++++++ hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- ...rverCapabilityStatementProviderR4Test.java | 44 ++++++++--- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 92 files changed, 365 insertions(+), 155 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml create mode 100644 hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 5e53eca724d..d6f65b91794 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index ece0037127c..3d3a0683523 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 8230b4bef07..8f8e138ac7a 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index ab3faa6862d..780e528759e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -331,6 +331,9 @@ public class Constants { */ public static final int UUID_LENGTH = 36; + public static final String BULK_DATA_ACCESS_IG_URL = + "http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data"; + /** * Application configuration key used to enable or disable Hibernate Envers. */ diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index d5a6c2c70d6..fb52d568b9f 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 718fa973873..7b4c1afe91d 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.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 a4701a94456..cbd07617e0f 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 9f3a84bf061..982c6c46480 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 2c6dd8583ab..cd84edbca81 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 28b5ede3287..08d53f813c0 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 0e8df018a33..38cac3e4603 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index cfa706d8794..c067f7e19c9 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index f5976ab3234..bef8a3b2810 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 331d4b20add..e6042b475df 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml new file mode 100644 index 00000000000..7cdc6abc6ed --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5330 +jira: SMILE-7372 +title: "Previously, the capability statement returned by the server would not declare conformance to IG when a bulk data +export provider is registered with the server. This issue has been fixed." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 7e8139bd23c..5e82fe2a42b 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index d39e783edf1..9ed9b7cc147 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 8c6b15cd275..cd0d62fd93a 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index f0789864dd9..223eb3c1ffa 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index aa1db0b0a66..6f7859823db 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 6362de695c2..452671378ab 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 88fef02fb1c..c48e50f0fc4 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 521442039ec..3ee942fbcd1 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 8e4fec76eb1..27c8d0a0379 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index 7b62541c178..333a962df0d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -166,14 +166,6 @@ public class JpaConstants { * Operation name for the "$import-poll-status" operation */ public static final String OPERATION_IMPORT_POLL_STATUS = "$import-poll-status"; - /** - * Operation name for the "$export" operation - */ - public static final String OPERATION_EXPORT = "$export"; - /** - * Operation name for the "$export-poll-status" operation - */ - public static final String OPERATION_EXPORT_POLL_STATUS = "$export-poll-status"; /** * Operation name for the "$lastn" operation */ @@ -310,6 +302,7 @@ public class JpaConstants { "https://hapifhir.org/NamingSystem/bulk-export-binary-resource-type"; public static final Set UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE = Set.of("Provenance", "List", "Group"); + /** * Non-instantiable */ diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 3fd487db90d..3fe94ad2c5f 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 5beeda86ad3..972d0aca525 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 - 6.9.7-SNAPSHOT + 6.9.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 12677842d4b..6fe814dfd00 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 663c4258d5b..de5da25656a 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 7d27d009288..74e8f394246 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java index 4b354894d7d..bd8b9fedc49 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; @@ -192,7 +193,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // test - HttpPost post = new HttpPost(myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -233,7 +234,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); Parameters input = new Parameters(); - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -261,7 +262,7 @@ public class BulkDataExportProviderTest { } else { myBaseUrl = myServer.getBaseUrl(); } - String url = myBaseUrl + "/" + JpaConstants.OPERATION_EXPORT + String url = myBaseUrl + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()) @@ -290,7 +291,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.startInstance(isNotNull(), any())) .thenReturn(createJobStartResponse()); - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient,EpisodeOfCare") + "&" + JpaConstants.PARAM_EXPORT_TYPE_FILTER + "=" + UrlUtil.escapeUrlParam("Patient?_id=P999999990") @@ -330,7 +331,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -362,7 +363,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -421,7 +422,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -490,7 +491,7 @@ public class BulkDataExportProviderTest { // call String myBaseUriForExport = myServer.getBaseUrl() + "/Partition-B"; - String url = myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -527,7 +528,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -553,7 +554,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.getInstance(anyString())) .thenThrow(new ResourceNotFoundException("Unknown job: AAA")); - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -596,7 +597,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -625,7 +626,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); - String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()) @@ -660,7 +661,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); - String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()); @@ -693,7 +694,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); // manual construct - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Immunization, Observation") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()); @@ -726,7 +727,7 @@ public class BulkDataExportProviderTest { public void testInitiateGroupExportWithInvalidResourceTypesFails() throws IOException { // when - String url = myServer.getBaseUrl() + "/" + "Group/123/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + "Group/123/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("StructureDefinition,Observation"); @@ -747,7 +748,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.startInstance(isNotNull(), any())).thenReturn(createJobStartResponse()); // test - String url = myServer.getBaseUrl() + "/" + "Group/123/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + "Group/123/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON); HttpGet get = new HttpGet(url); @@ -779,7 +780,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -808,7 +809,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(Constants.CT_FHIR_NDJSON)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -841,7 +842,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -874,7 +875,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient, Practitioner")); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.addHeader(Constants.HEADER_CACHE_CONTROL, Constants.CACHE_CONTROL_NO_CACHE); post.setEntity(new ResourceEntity(myCtx, input)); @@ -908,7 +909,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient, Practitioner")); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -979,7 +980,7 @@ public class BulkDataExportProviderTest { baseUrl = myServer.getBaseUrl(); } - String url = baseUrl + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = baseUrl + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpDelete delete = new HttpDelete(url); try (CloseableHttpResponse response = myClient.execute(delete)) { @@ -1011,7 +1012,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpDelete delete = new HttpDelete(url); try (CloseableHttpResponse response = myClient.execute(delete)) { @@ -1035,7 +1036,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); // call - final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s", myServer.getPort(), JpaConstants.OPERATION_EXPORT)); + final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s", myServer.getPort(), ProviderConstants.OPERATION_EXPORT)); httpGet.addHeader("_outputFormat", Constants.CT_FHIR_NDJSON); httpGet.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -1058,7 +1059,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); // call - final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s?_outputFormat=%s", myServer.getPort(), JpaConstants.OPERATION_EXPORT, Constants.CT_FHIR_NDJSON)); + final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s?_outputFormat=%s", myServer.getPort(), ProviderConstants.OPERATION_EXPORT, Constants.CT_FHIR_NDJSON)); httpGet.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); try (CloseableHttpResponse response = myClient.execute(httpGet)) { @@ -1086,7 +1087,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID, new StringType(jobId)); // Initiate Export Poll Status - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1130,7 +1131,7 @@ public class BulkDataExportProviderTest { } // Initiate Export Poll Status - HttpPost post = new HttpPost(baseUrl + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(baseUrl + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1148,7 +1149,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(ca.uhn.fhir.rest.api.Constants.CT_FHIR_NDJSON)); // Initiate Export Poll Status - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1160,7 +1161,7 @@ public class BulkDataExportProviderTest { private void callExportAndAssertJobId(Parameters input, String theExpectedJobId) throws IOException { HttpPost post; - post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.addHeader(Constants.HEADER_CACHE_CONTROL, Constants.CACHE_CONTROL_NO_CACHE); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1180,7 +1181,7 @@ public class BulkDataExportProviderTest { enablePartitioning(); // test - String url = myServer.getBaseUrl() + "/Partition-B/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/Partition-B/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner"); @@ -1214,7 +1215,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/Partition-B/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/Partition-B/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java index d6224766797..808c8ee6736 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java @@ -19,6 +19,7 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.JsonUtil; @@ -618,11 +619,11 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(Constants.CT_FHIR_NDJSON)); input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient")); - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); - HttpGet get = new HttpGet(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + "?_outputFormat=application%2Ffhir%2Bndjson&_type=Patient"); + HttpGet get = new HttpGet(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?_outputFormat=application%2Ffhir%2Bndjson&_type=Patient"); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); try(CloseableHttpResponse postResponse = mySender.execute(post)){ ourLog.info("Response: {}",postResponse); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java index d4119ef70e9..9d1c1ee1421 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.util.JsonUtil; @@ -1452,7 +1453,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onInstance("Group/" + theGroupOrPatientId) - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) @@ -1463,7 +1464,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onInstance("Patient/" + theGroupOrPatientId) - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) @@ -1473,7 +1474,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onServer() - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java index 45c93f32803..d0a1c576f1c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java @@ -1,15 +1,12 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; -import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_EXPORT; +import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_EXPORT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 64d0faeba73..11cd5638c18 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.util.JsonUtil; @@ -713,7 +714,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te } private String buildExportUrl(String createInPartition, String jobId) { - return myClient.getServerBase() + "/" + createInPartition + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + return myClient.getServerBase() + "/" + createInPartition + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + jobId; } } diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index fd24ac730a3..ffe2b31be48 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 5ce759bc927..2e85a2a2bd4 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 39be13b38d2..0c810ee1268 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index e5b8861c32f..883d501d672 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 93e6f3d05a2..b3c0379ccd7 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index b541b0f0a2c..03faeaa9b70 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 49c0ef80517..97a9d442082 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 8bf19a63b51..05f1e323a7c 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java index f5ceb74ec62..7e0e4235a44 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java @@ -231,4 +231,13 @@ public class ProviderConstants { */ @Deprecated public static final String PERFORM_REINDEXING_PASS = "$perform-reindexing-pass"; + + /** + * Operation name for the "$export-poll-status" operation + */ + public static final String OPERATION_EXPORT_POLL_STATUS = "$export-poll-status"; + /** + * Operation name for the "$export" operation + */ + public static final String OPERATION_EXPORT = "$export"; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java index b7532e20d5d..d07a60d8366 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java @@ -536,12 +536,27 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv } } + maybeAddBulkDataDeclarationToConformingToIg(terser, retVal, configuration.getServerBindings()); + postProcessRest(terser, rest); postProcess(terser, retVal); return retVal; } + private void maybeAddBulkDataDeclarationToConformingToIg( + FhirTerser theTerser, IBaseConformance theBaseConformance, List theServerBindings) { + boolean bulkExportEnabled = theServerBindings.stream() + .filter(OperationMethodBinding.class::isInstance) + .map(OperationMethodBinding.class::cast) + .map(OperationMethodBinding::getName) + .anyMatch(ProviderConstants.OPERATION_EXPORT::equals); + + if (bulkExportEnabled) { + theTerser.addElement(theBaseConformance, "instantiates", Constants.BULK_DATA_ACCESS_IG_URL); + } + } + /** * * @param theSearchParam diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 3ce49430a7c..3d61960b0b0 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 - 6.9.7-SNAPSHOT + 6.9.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 11e3f12b2f8..b3446ed9a2a 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.7-SNAPSHOT + 6.9.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 a149f89c490..ab639b80f69 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 - 6.9.7-SNAPSHOT + 6.9.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 31c40af7b65..bd046a5d4a5 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 660ad79aacc..c465731f954 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.9.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 f9079b8cfab..3cd4f85ea30 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 - 6.9.7-SNAPSHOT + 6.9.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 fb4d0312cc7..36d820541cf 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 - 6.9.7-SNAPSHOT + 6.9.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 dbe096cd8c5..cdf05f46650 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 - 6.9.7-SNAPSHOT + 6.9.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 2f669d15070..27dc8f1ee62 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 - 6.9.7-SNAPSHOT + 6.9.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 83374866bf0..7f01939e11a 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 - 6.9.7-SNAPSHOT + 6.9.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 01b07e351b5..59b94d03c74 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 93c95a33faf..465d0b784d3 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 88816897c56..50f71fb0834 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 4dddaeb11f5..da9f668f778 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 - 6.9.7-SNAPSHOT + 6.9.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 9fd2ef9d265..f35052d3156 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 @@ -52,6 +52,7 @@ import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.ArrayUtil; @@ -125,10 +126,11 @@ public class BulkDataExportProvider { * $export */ @Operation( - name = JpaConstants.OPERATION_EXPORT, + name = ProviderConstants.OPERATION_EXPORT, global = false /* set to true once we can handle this */, manualResponse = true, - idempotent = true) + idempotent = true, + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/export") public void export( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @@ -152,7 +154,7 @@ public class BulkDataExportProvider { IPrimitiveType theExportId, ServletRequestDetails theRequestDetails) { // JPA export provider - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); BulkExportJobParameters BulkExportJobParameters = buildSystemBulkExportOptions( theOutputFormat, theType, theSince, theTypeFilter, theExportId, theTypePostFetchFilterUrl); @@ -212,7 +214,12 @@ public class BulkDataExportProvider { /** * Group/[id]/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Group") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Group", + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/group-export") public void groupExport( @IdParam IIdType theIdParam, @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") @@ -244,7 +251,7 @@ public class BulkDataExportProvider { ourLog.debug("_typeFilter={}", theTypeFilter); ourLog.debug("_mdm={}", theMdm); - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); // verify the Group exists before starting the job validateTargetsExists(theRequestDetails, "Group", List.of(theIdParam)); @@ -322,7 +329,12 @@ public class BulkDataExportProvider { /** * Patient/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Patient", + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/patient-export") public void patientExport( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @@ -351,7 +363,7 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); if (thePatient != null) { validateTargetsExists( @@ -376,7 +388,11 @@ public class BulkDataExportProvider { /** * Patient/[id]/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Patient") public void patientInstanceExport( @IdParam IIdType theIdParam, @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") @@ -400,7 +416,7 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); validateTargetsExists(theRequestDetails, "Patient", List.of(theIdParam)); @@ -422,7 +438,7 @@ public class BulkDataExportProvider { */ @SuppressWarnings("unchecked") @Operation( - name = JpaConstants.OPERATION_EXPORT_POLL_STATUS, + name = ProviderConstants.OPERATION_EXPORT_POLL_STATUS, manualResponse = true, idempotent = true, deleteEnabled = true) @@ -712,7 +728,7 @@ public class BulkDataExportProvider { if (serverBase == null) { throw new InternalErrorException(Msg.code(2136) + "Unable to get the server base."); } - String pollLocation = serverBase + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + String pollLocation = serverBase + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + theInstanceId; pollLocation = UrlUtil.sanitizeHeaderValue(pollLocation); diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 428fe50b7e9..4b55a6d0612 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 6f750898ab8..1e2ba7f7e8c 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index bf135680f03..c7b6ee03823 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 0158106fa8b..b815a275b9f 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index db577ff2395..e665b690315 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index db2d2d1c0cb..bb9a01ab472 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.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 4551585acde..6f9ae35ffb8 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index c1e21dc8588..a9643757fc7 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 631fe4f5b37..cda18bff14a 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java index eaf2a98c582..c4ed58392ed 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -30,25 +30,57 @@ import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.Bindings; +import ca.uhn.fhir.rest.server.IServerConformanceProvider; +import ca.uhn.fhir.rest.server.ResourceBinding; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.RestfulServerConfiguration; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import ca.uhn.fhir.rest.server.method.IParameter; +import ca.uhn.fhir.rest.server.method.OperationMethodBinding; import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; +import ca.uhn.fhir.rest.server.method.OperationParameter; +import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; -import ca.uhn.fhir.rest.server.*; -import ca.uhn.fhir.rest.server.method.*; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.dstu3.model.CapabilityStatement; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementKind; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.ConditionalDeleteStatus; +import org.hl7.fhir.dstu3.model.CapabilityStatement.ResourceInteractionComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.RestfulCapabilityMode; +import org.hl7.fhir.dstu3.model.CapabilityStatement.SystemRestfulInteraction; +import org.hl7.fhir.dstu3.model.CapabilityStatement.TypeRestfulInteraction; +import org.hl7.fhir.dstu3.model.CapabilityStatement.UnknownContentCode; +import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.OperationDefinition; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind; -import org.hl7.fhir.dstu3.model.*; -import org.hl7.fhir.dstu3.model.CapabilityStatement.*; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.ResourceType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import java.util.*; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -384,6 +416,8 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } } + maybeAddBulkDataDeclarationToConformingToIg(retVal, serverConfiguration.getServerBindings()); + return retVal; } @@ -682,4 +716,17 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } }); } + + private void maybeAddBulkDataDeclarationToConformingToIg( + CapabilityStatement theCapabilityStatement, List theServerBindings) { + boolean bulkExportEnabled = theServerBindings.stream() + .filter(OperationMethodBinding.class::isInstance) + .map(OperationMethodBinding.class::cast) + .map(OperationMethodBinding::getName) + .anyMatch(ProviderConstants.OPERATION_EXPORT::equals); + + if (bulkExportEnabled) { + theCapabilityStatement.addInstantiates(Constants.BULK_DATA_ACCESS_IG_URL); + } + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java index 6942d049006..916c47c286b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.RestfulServer; @@ -40,6 +41,8 @@ import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.IParameter; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; +import ca.uhn.fhir.rest.server.provider.BulkDataExportProvider; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.FhirValidator; @@ -81,6 +84,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -846,6 +850,24 @@ public class ServerCapabilityStatementProviderDstu3Test { assertThat(patientResource.getProfile().getReference(), containsString(PATIENT_SUB)); } + @Test + public void testMethodGetServerConformance_whenServerSupportsExportOperation_willIncludeInstantiatesElement() throws Exception { + // given + RestfulServer rs = new RestfulServer(ourCtx); + rs.setProviders(new BulkDataExportProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR3")); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); + rs.setServerConformanceProvider(sc); + + // when + rs.init(createServletConfig()); + CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + // then + String instantiatesFirstRepValue = conformance.getInstantiates().get(0).getValue(); + assertThat(instantiatesFirstRepValue, equalTo(Constants.BULK_DATA_ACCESS_IG_URL)); + } + private List toOperationIdParts(List theOperation) { ArrayList retVal = Lists.newArrayList(); for (CapabilityStatementRestOperationComponent next : theOperation) { diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 8fc2cd71f11..e0567ffd108 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 59b166ee958..e070a8fb9e7 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index d29ea3d5697..5b9471961cb 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 06a05fad87f..c97208fe2e2 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index a487a4111e7..f208de1b2f7 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java new file mode 100644 index 00000000000..0aa762af2a0 --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java @@ -0,0 +1,76 @@ +package ca.uhn.fhir.rest.server.provider; + +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import java.io.IOException; +import java.util.Date; + +public class BulkDataExportProvider { + + public static final String PARAM_EXPORT_POLL_STATUS_JOB_ID = "_jobId"; + public static final String PARAM_EXPORT_OUTPUT_FORMAT = "_outputFormat"; + public static final String PARAM_EXPORT_TYPE = "_type"; + public static final String PARAM_EXPORT_SINCE = "_since"; + + public static final String PARAM_EXPORT_TYPE_FILTER = "_typeFilter"; + public static final String PARAM_EXPORT_MDM = "_mdm"; + + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + global = false /* set to true once we can handle this */, + manualResponse = true, + idempotent = true) + public void export( + @OperationParam(name = PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") + IPrimitiveType theOutputFormat, + @OperationParam(name = PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") + IPrimitiveType theType, + @OperationParam(name = PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") + IPrimitiveType theSince, + @OperationParam(name = PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") + IPrimitiveType theTypeFilter, + ServletRequestDetails theRequestDetails) {} + + @Operation(name = ProviderConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Group") + public void groupExport( + @IdParam IIdType theIdParam, + @OperationParam(name = PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") + IPrimitiveType theOutputFormat, + @OperationParam(name = PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") + IPrimitiveType theType, + @OperationParam(name = PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") + IPrimitiveType theSince, + @OperationParam(name = PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") + IPrimitiveType theTypeFilter, + @OperationParam(name = PARAM_EXPORT_MDM, min = 0, max = 1, typeName = "boolean") + IPrimitiveType theMdm, + ServletRequestDetails theRequestDetails) {} + + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Patient") + public void patientExport( + @OperationParam(name = PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") + IPrimitiveType theOutputFormat, + @OperationParam(name = PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") + IPrimitiveType theType, + @OperationParam(name = PARAM_EXPORT_SINCE, min = 0, max = 1, typeName = "instant") + IPrimitiveType theSince, + @OperationParam(name = PARAM_EXPORT_TYPE_FILTER, min = 0, max = 1, typeName = "string") + IPrimitiveType theTypeFilter, + ServletRequestDetails theRequestDetails) {} + + @Operation(name = ProviderConstants.OPERATION_EXPORT_POLL_STATUS, manualResponse = true, idempotent = true) + public void exportPollStatus( + @OperationParam(name = PARAM_EXPORT_POLL_STATUS_JOB_ID, typeName = "string", min = 0, max = 1) + IPrimitiveType theJobId, + ServletRequestDetails theRequestDetails) + throws IOException {} +} diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 36d9abd066b..fb872aa9377 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 - 6.9.7-SNAPSHOT + 6.9.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 b6d77ec34a0..77f87de8553 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 - 6.9.7-SNAPSHOT + 6.9.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 43aa5865752..0c33abe7f82 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 - 6.9.7-SNAPSHOT + 6.9.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 eafcb813fb3..1d399034981 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 - 6.9.7-SNAPSHOT + 6.9.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 0c138e4467e..fb0365de8a7 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 - 6.9.7-SNAPSHOT + 6.9.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 4c7adc28391..8d78cb03be9 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 - 6.9.7-SNAPSHOT + 6.9.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 74a9270ff65..5018a5c0a0c 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 6279919ef8f..f7c7a9fcfa1 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java index 41ce1ffcd31..7cd6a5d4847 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java @@ -96,7 +96,9 @@ import java.util.Set; import java.util.stream.Collectors; import static ca.uhn.fhir.test.utilities.server.MockServletUtil.createServletConfig; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; @@ -212,6 +214,24 @@ public class ServerCapabilityStatementProviderR4Test { assertTrue(res.getConditionalUpdate()); } + @Test + public void testMethodGetServerConformance_whenServerSupportsExportOperation_willIncludeInstantiatesElement() throws Exception { + // given + RestfulServer rs = new RestfulServer(myCtx); + rs.setProviders(new BulkDataExportProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + rs.setServerConformanceProvider(sc); + + // when + rs.init(createServletConfig()); + CapabilityStatement conformance = (CapabilityStatement) sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + // then + String instantiatesFirstRepValue = conformance.getInstantiates().get(0).getValue(); + assertThat(instantiatesFirstRepValue, equalTo(Constants.BULK_DATA_ACCESS_IG_URL)); + } + private RequestDetails createRequestDetails(RestfulServer theServer) { ServletRequestDetails retVal = new ServletRequestDetails(); retVal.setServer(theServer); @@ -1274,7 +1294,7 @@ public class ServerCapabilityStatementProviderR4Test { @Test public void testBulkDataExport() throws ServletException { RestfulServer rs = new RestfulServer(myCtx); - rs.setResourceProviders(new BulkDataExportProvider()); + rs.setProviders(new BulkDataExportProvider()); rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR4")); ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); @@ -1289,7 +1309,13 @@ public class ServerCapabilityStatementProviderR4Test { .filter(resource -> "Group".equals(resource.getType())) .findFirst().get(); - assertThat(toOperationNames(groupResource.getOperation()),containsInAnyOrder("export", "export-poll-status")); + boolean hasExportPollStatusOperationAtSystemLevel = conformance.getRestFirstRep().getOperation().stream() + .filter(CapabilityStatementRestResourceOperationComponent::hasName) + .map(CapabilityStatementRestResourceOperationComponent::getName) + .filter("export-poll-status"::equals).findFirst().isPresent(); + + assertThat(toOperationNames(groupResource.getOperation()),contains("export")); + assertThat(hasExportPollStatusOperationAtSystemLevel, is(true)); } @Test @@ -1353,8 +1379,8 @@ public class ServerCapabilityStatementProviderR4Test { return myCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(theResource); } - public static class BulkDataExportProvider implements IResourceProvider { - @Operation(name = JpaConstants.OPERATION_EXPORT, global = false /* set to true once we can handle this */, manualResponse = true, idempotent = true) + public static class BulkDataExportProvider { + @Operation(name = ProviderConstants.OPERATION_EXPORT, global = false /* set to true once we can handle this */, manualResponse = true, idempotent = true) public void export( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType theType, @@ -1364,7 +1390,7 @@ public class ServerCapabilityStatementProviderR4Test { ) { } - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Group") + @Operation(name = ProviderConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Group") public void groupExport( @IdParam IIdType theIdParam, @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @@ -1376,7 +1402,7 @@ public class ServerCapabilityStatementProviderR4Test { ) { } - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") + @Operation(name = ProviderConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") public void patientExport( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @OperationParam(name = JpaConstants.PARAM_EXPORT_TYPE, min = 0, max = 1, typeName = "string") IPrimitiveType theType, @@ -1386,17 +1412,13 @@ public class ServerCapabilityStatementProviderR4Test { ) { } - @Operation(name = JpaConstants.OPERATION_EXPORT_POLL_STATUS, manualResponse = true, idempotent = true) + @Operation(name = ProviderConstants.OPERATION_EXPORT_POLL_STATUS, manualResponse = true, idempotent = true) public void exportPollStatus( @OperationParam(name = JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID, typeName = "string", min = 0, max = 1) IPrimitiveType theJobId, ServletRequestDetails theRequestDetails ) throws IOException { } - @Override - public Class getResourceType() { - return Group.class; - } } @SuppressWarnings("unused") diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 5cf5243555b..2880a0397e4 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 856b520a69d..133b6269e46 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 0214d4a609b..a9bf58d73ad 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.9.7-SNAPSHOT + 6.9.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 6313b5e1841..3d54ad5b0c4 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 - 6.9.7-SNAPSHOT + 6.9.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 9403c03441b..11f932aa409 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 - 6.9.7-SNAPSHOT + 6.9.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 81318121d99..2bac2e98877 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 - 6.9.7-SNAPSHOT + 6.9.8-SNAPSHOT ../../pom.xml From a2204654df6d4e045a8984edbe3c5d8d289e8fbf Mon Sep 17 00:00:00 2001 From: Brenin Rhodes Date: Wed, 27 Sep 2023 11:34:57 -0600 Subject: [PATCH 02/86] Implement CDS on FHIR in CDS Hooks (#5240) * WIP * WIP refactor * Initial commit * measureRefactor WIP * version bump * Bump to core release 6.0.22 (#5028) * Bump to core release 6.0.16 * Bump to core version 6.0.20 * Fix errors thrown as a result of VersionSpecificWorkerContextWrapper * Bump to core 6.0.22 * adding tests WIP * Rework MeasureServiceFActory * measure refactor WIP * Resolve 5126 hfj res ver prov might cause migration error on db that automatically indexes the primary key (#5127) * dropped old index FK_RESVERPROV_RES_PID on RES_PID column before adding IDX_RESVERPROV_RES_PID * added changelog * changed to valid version number * changed to valid version number, need to be ordered by version number... * generic provider loader * provider loader update * tests wip * 5123 - Use DEFAULT partition for server-based requests if none specified (#5124) 5123 - Use DEFAULT partition for server-based requests if none specified * consent remove all suppresses next link in bundle (#5119) * added FIXME with source of issue * added FIXME with root cause * added FIXME with root cause * Providing solution to the issue and removing fixmes. * Providing changelog * auto-formatting. * Adding new test. * Adding a new test for standard paging * let's try this and see if it works...? * fix tests * cleanup to trigger a new run * fixing tests --------- Co-authored-by: Ken Stevens Co-authored-by: peartree * 5117 MDM Score for No Match Fields Should Not Be Included in Total Score (#5118) * fix, test, changelog * fix, test, changelog --------- Co-authored-by: justindar * measureRefactor tests WIP * update tests wip * remove baseCrR4Test * update imports * add paging provider config to tests * dstu3 tests * _source search parameter needs to support modifiers (#5095) _source search parameter needs to support modifiers - added support form :contains, :missing, :above modifiers * latest work * cleanup * Refactor operation providers to support use in multiple modules * Fix HFQL docs (#5151) * repository measure refactor * undo bundle edit, add config * embedded library true * undo post of bundle * clear library cache on test * fix sumbit-data test * Fix tests * cleanup * Update MeasureService.java * latest * 3.0.0 engine update * merge cleanup * cr updates * cleanup * Only create services for PlanDefs that have a trigger with a named event * Fix parameters being sent through invoke * Handle system actions * cleanup * missing packages from cql * fix test data and test cases for repository api * latest * fix pom * fix test config * cleanup * 3.0.0 clinical reasoning uplift * r4 cql execution provider and tests * fix submitdata provider config * wip cql tests * debugging cql op wip * wip debugging cql * update tests for $cql, fix class names * prep for Pre6 uplift * spotless checks and test fixes * bump to 3.0.0-PRE6 * cleanup * fix version * cleanup * cleanup * fix exlusions in pom * add in cache invalidation config * fix resource resolution * cleanup * search converter bug and test for repository * update pom * update searchconverter * version bump, add changelog * remove term config, move IDaoRegistryUser class * Break out dstu3 providers and move config to version folders * merge cleanup * merge cleanup * Add changelog * spotless * fix error codes * cleanup * cleanup * Handle missing beans in Operation configs * spotless * Use CondtionalOnBean for CR Operation and Repository configs * Fix test config * Add RepositoryFactory and RestfulServer to CdsConfigService * Move createRequestDetails into CdsConfigService * spotless * review comments --------- Co-authored-by: justin.mckelvy Co-authored-by: tadgh Co-authored-by: dotasek Co-authored-by: Jonathan Percival Co-authored-by: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: Nathan Doef Co-authored-by: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> --- .../changelog/7_0_0/5238-add-cds-on-fhir.yaml | 6 + hapi-fhir-server-cds-hooks/pom.xml | 5 + .../fhir/cdshooks/api/ICdsConfigService.java | 24 + .../cdshooks/api/ICdsServiceRegistry.java | 8 + .../fhir/cdshooks/config/CdsHooksConfig.java | 96 +- .../fhir/cdshooks/svc/BaseCdsCrMethod.java | 47 + .../cdshooks/svc/CdsConfigServiceImpl.java | 22 +- .../fhir/cdshooks/svc/CdsCrServiceMethod.java | 45 + .../fhir/cdshooks/svc/CdsServiceCache.java | 21 + .../cdshooks/svc/CdsServiceRegistryImpl.java | 21 +- .../fhir/cdshooks/svc/cr/CdsCrConstants.java | 48 + .../cdshooks/svc/cr/CdsCrServiceDstu3.java | 291 ++++ .../fhir/cdshooks/svc/cr/CdsCrServiceR4.java | 334 ++++ .../fhir/cdshooks/svc/cr/CdsCrServiceR5.java | 337 ++++ .../hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java | 41 + .../svc/cr/CdsServiceInterceptor.java | 93 ++ .../fhir/cdshooks/svc/cr/ICdsCrService.java | 82 + .../cdshooks/svc/cr/ICdsCrServiceFactory.java | 24 + .../cr/discovery/CrDiscoveryElementDstu3.java | 82 + .../cr/discovery/CrDiscoveryElementR4.java | 82 + .../cr/discovery/CrDiscoveryElementR5.java | 82 + .../cr/discovery/CrDiscoveryServiceDstu3.java | 445 +++++ .../cr/discovery/CrDiscoveryServiceR4.java | 429 +++++ .../cr/discovery/CrDiscoveryServiceR5.java | 431 +++++ .../svc/cr/discovery/ICrDiscoveryElement.java | 30 + .../svc/cr/discovery/ICrDiscoveryService.java | 26 + .../discovery/ICrDiscoveryServiceFactory.java | 24 + .../svc/cr/discovery/PrefetchUrlList.java | 45 + .../cdshooks/svc/prefetch/CdsPrefetchSvc.java | 6 + .../hapi/fhir/cdshooks/svc/cr/BaseCrTest.java | 17 + .../fhir/cdshooks/svc/cr/TestCrConfig.java | 14 + .../discovery/CrDiscoveryServiceR4Test.java | 44 + .../svc/cr/resolution/CdsCrServiceR4Test.java | 78 + .../test/resources/ASLPCrdServiceRequest.json | 87 + .../resources/Bundle-ASLPCrd-Content.json | 1450 +++++++++++++++++ .../resources/Bundle-ASLPCrd-Response.json | 388 +++++ ...dle-DischargeInstructionsPlan-Content.json | 161 ++ ...le-DischargeInstructionsPlan-Response.json | 87 + .../rest/api/server/SystemRequestDetails.java | 6 + .../api/server/SystemRestfulResponse.java | 74 + hapi-fhir-storage-cr/pom.xml | 6 + .../uhn/fhir/cr/config/RepositoryConfig.java | 2 + .../cr/config/dstu3/ApplyOperationConfig.java | 6 +- .../config/dstu3/ExtractOperationConfig.java | 5 +- .../config/dstu3/PackageOperationConfig.java | 5 +- .../config/dstu3/PopulateOperationConfig.java | 5 +- .../cr/config/r4/ApplyOperationConfig.java | 6 +- .../cr/config/r4/ExtractOperationConfig.java | 5 +- .../cr/config/r4/PackageOperationConfig.java | 5 +- .../cr/config/r4/PopulateOperationConfig.java | 5 +- .../fhir/cr/repo/RequestDetailsCloner.java | 6 +- .../ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java | 12 +- .../ca/uhn/fhir/cr/r4/TestCrR4Config.java | 7 +- 53 files changed, 5685 insertions(+), 23 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java create mode 100644 hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java create mode 100644 hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java create mode 100644 hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java create mode 100644 hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java create mode 100644 hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json create mode 100644 hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json create mode 100644 hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json create mode 100644 hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json create mode 100644 hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json create mode 100644 hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml new file mode 100644 index 00000000000..38bddd5b239 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5238 +title: "Added an implementation of Clinical Reasoning CDS on FHIR to the CDS Hooks module that allows PlanDefinition +worfklows to be processed as CDS Services using the $apply operation. +" diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index b3c0379ccd7..feac29e26be 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -33,6 +33,11 @@ hapi-fhir-storage ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-storage-cr + ${project.version} + org.springframework diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java index 78f74247e0d..705205be473 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java @@ -20,8 +20,14 @@ package ca.uhn.hapi.fhir.cdshooks.api; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRestfulResponse; +import ca.uhn.fhir.rest.server.RestfulServer; import com.fasterxml.jackson.databind.ObjectMapper; +import org.opencds.cqf.fhir.utility.Ids; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -37,4 +43,22 @@ public interface ICdsConfigService { default DaoRegistry getDaoRegistry() { return null; } + + @Nullable + default IRepositoryFactory getRepositoryFactory() { + return null; + } + + @Nullable + default RestfulServer getRestfulServer() { + return null; + } + + default RequestDetails createRequestDetails(FhirContext theFhirContext, String theId, String theResourceType) { + SystemRequestDetails rd = new SystemRequestDetails(); + rd.setServer(getRestfulServer()); + rd.setResponse(new SystemRestfulResponse(rd)); + rd.setId(Ids.newId(theFhirContext.getVersion().getVersion(), theResourceType, theId)); + return rd; + } } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java index a979ce49557..869b38f23fb 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java @@ -72,6 +72,14 @@ public interface ICdsServiceRegistry { boolean theAllowAutoFhirClientPrefetch, String theModuleId); + /** + * Register a new Clinical Reasoning CDS Service with the endpoint. + * + * @param theServiceId the id of the service PlanDefinition + * @return the service was registered + */ + boolean registerCrService(String theServiceId); + /** * Remove registered CDS service with the service ID, only removes dynamically registered service * diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java index 737a341f460..f5bd894f645 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java @@ -20,8 +20,13 @@ package ca.uhn.hapi.fhir.cdshooks.config; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry; @@ -29,11 +34,21 @@ import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceDstu3; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR5; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsServiceInterceptor; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceDstu3; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceR4; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceR5; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchDaoSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchFhirClientSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsResolutionStrategySvc; import com.fasterxml.jackson.databind.ObjectMapper; +import org.opencds.cqf.fhir.api.Repository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; @@ -44,12 +59,23 @@ public class CdsHooksConfig { public static final String CDS_HOOKS_OBJECT_MAPPER_FACTORY = "cdsHooksObjectMapperFactory"; + public static final String PLAN_DEFINITION_RESOURCE_NAME = "PlanDefinition"; + @Autowired(required = false) private DaoRegistry myDaoRegistry; @Autowired(required = false) private MatchUrlService myMatchUrlService; + @Autowired(required = false) + private IResourceChangeListenerRegistry myResourceChangeListenerRegistry; + + @Autowired(required = false) + private IRepositoryFactory myRepositoryFactory; + + @Autowired(required = false) + private RestfulServer myRestfulServer; + @Bean(name = CDS_HOOKS_OBJECT_MAPPER_FACTORY) public ObjectMapper objectMapper(FhirContext theFhirContext) { return new CdsHooksObjectMapperFactory(theFhirContext).newMapper(); @@ -59,14 +85,78 @@ public class CdsHooksConfig { public ICdsServiceRegistry cdsServiceRegistry( CdsHooksContextBooter theCdsHooksContextBooter, CdsPrefetchSvc theCdsPrefetchSvc, - @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper) { - return new CdsServiceRegistryImpl(theCdsHooksContextBooter, theCdsPrefetchSvc, theObjectMapper); + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + ICdsCrServiceFactory theCdsCrServiceFactory, + ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory) { + return new CdsServiceRegistryImpl( + theCdsHooksContextBooter, + theCdsPrefetchSvc, + theObjectMapper, + theCdsCrServiceFactory, + theCrDiscoveryServiceFactory); + } + + @Bean + public ICdsCrServiceFactory cdsCrServiceFactory(FhirContext theFhirContext, ICdsConfigService theCdsConfigService) { + return id -> { + if (myRepositoryFactory == null) { + return null; + } + RequestDetails rd = + theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); + Repository repository = myRepositoryFactory.create(rd); + switch (theFhirContext.getVersion().getVersion()) { + case DSTU3: + return new CdsCrServiceDstu3(rd, repository); + case R4: + return new CdsCrServiceR4(rd, repository); + case R5: + return new CdsCrServiceR5(rd, repository); + default: + return null; + } + }; + } + + @Bean + public ICrDiscoveryServiceFactory crDiscoveryServiceFactory( + FhirContext theFhirContext, ICdsConfigService theCdsConfigService) { + return id -> { + if (myRepositoryFactory == null) { + return null; + } + RequestDetails rd = + theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); + Repository repository = myRepositoryFactory.create(rd); + switch (theFhirContext.getVersion().getVersion()) { + case DSTU3: + return new CrDiscoveryServiceDstu3(rd.getId(), repository); + case R4: + return new CrDiscoveryServiceR4(rd.getId(), repository); + case R5: + return new CrDiscoveryServiceR5(rd.getId(), repository); + default: + return null; + } + }; + } + + @Bean + public CdsServiceInterceptor cdsServiceInterceptor() { + if (myResourceChangeListenerRegistry == null) { + return null; + } + CdsServiceInterceptor listener = new CdsServiceInterceptor(); + myResourceChangeListenerRegistry.registerResourceResourceChangeListener( + PLAN_DEFINITION_RESOURCE_NAME, SearchParameterMap.newSynchronous(), listener, 1000); + return listener; } @Bean public ICdsConfigService cdsConfigService( FhirContext theFhirContext, @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper) { - return new CdsConfigServiceImpl(theFhirContext, theObjectMapper, myDaoRegistry); + return new CdsConfigServiceImpl( + theFhirContext, theObjectMapper, myDaoRegistry, myRepositoryFactory, myRestfulServer); } @Bean diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java new file mode 100644 index 00000000000..53f43879fad --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.IModelJson; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + +abstract class BaseCdsCrMethod implements ICdsMethod { + private ICdsCrServiceFactory myCdsCrServiceFactory; + + public BaseCdsCrMethod(ICdsCrServiceFactory theCdsCrServiceFactory) { + myCdsCrServiceFactory = theCdsCrServiceFactory; + } + + public Object invoke(ObjectMapper theObjectMapper, IModelJson theJson, String theServiceId) { + try { + return myCdsCrServiceFactory.create(theServiceId).invoke(theJson); + } catch (Exception e) { + if (e.getCause() != null && e.getCause() instanceof BaseServerResponseException) { + throw (BaseServerResponseException) e.getCause(); + } + throw new ConfigurationException(Msg.code(2434) + "Failed to invoke $apply on " + theServiceId, e); + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java index c97b69f2760..59343edff07 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java @@ -20,7 +20,9 @@ package ca.uhn.hapi.fhir.cdshooks.svc; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,14 +33,20 @@ public class CdsConfigServiceImpl implements ICdsConfigService { private final FhirContext myFhirContext; private final ObjectMapper myObjectMapper; private final DaoRegistry myDaoRegistry; + private final IRepositoryFactory myRepositoryFactory; + private final RestfulServer myRestfulServer; public CdsConfigServiceImpl( @Nonnull FhirContext theFhirContext, @Nonnull ObjectMapper theObjectMapper, - @Nullable DaoRegistry theDaoRegistry) { + @Nullable DaoRegistry theDaoRegistry, + @Nullable IRepositoryFactory theRepositoryFactory, + @Nullable RestfulServer theRestfulServer) { myFhirContext = theFhirContext; myObjectMapper = theObjectMapper; myDaoRegistry = theDaoRegistry; + myRepositoryFactory = theRepositoryFactory; + myRestfulServer = theRestfulServer; } @Nonnull @@ -58,4 +66,16 @@ public class CdsConfigServiceImpl implements ICdsConfigService { public DaoRegistry getDaoRegistry() { return myDaoRegistry; } + + @Nullable + @Override + public IRepositoryFactory getRepositoryFactory() { + return myRepositoryFactory; + } + + @Nullable + @Override + public RestfulServer getRestfulServer() { + return myRestfulServer; + } } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java new file mode 100644 index 00000000000..98f41f2291e --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc; + +import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceMethod; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; + +public class CdsCrServiceMethod extends BaseCdsCrMethod implements ICdsServiceMethod { + private final CdsServiceJson myCdsServiceJson; + + public CdsCrServiceMethod(CdsServiceJson theCdsServiceJson, ICdsCrServiceFactory theCdsCrServiceFactory) { + super(theCdsCrServiceFactory); + myCdsServiceJson = theCdsServiceJson; + } + + @Override + public CdsServiceJson getCdsServiceJson() { + return myCdsServiceJson; + } + + @Override + public boolean isAllowAutoFhirClientPrefetch() { + // The $apply operation will make FHIR requests for any data it needs + // directly against the fhirServer of the ServiceRequest. + return false; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java index 660e9152288..eb89ff98bb6 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java @@ -25,6 +25,8 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +35,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_CR_MODULE_ID; + public class CdsServiceCache { static final Logger ourLog = LoggerFactory.getLogger(CdsServiceCache.class); final Map myServiceMap = new LinkedHashMap<>(); @@ -66,6 +70,23 @@ public class CdsServiceCache { } } + public void registerCrService( + String theServiceId, + ICrDiscoveryServiceFactory theDiscoveryServiceFactory, + ICdsCrServiceFactory theCrServiceFactory) { + if (!isCdsServiceAlreadyRegistered(theServiceId, CDS_CR_MODULE_ID)) { + CdsServiceJson cdsServiceJson = + theDiscoveryServiceFactory.create(theServiceId).resolveService(); + if (cdsServiceJson != null) { + final CdsCrServiceMethod cdsCrServiceMethod = + new CdsCrServiceMethod(cdsServiceJson, theCrServiceFactory); + myServiceMap.put(theServiceId, cdsCrServiceMethod); + myCdsServiceJson.addService(cdsServiceJson); + ourLog.info("Created service for {}", theServiceId); + } + } + } + public void registerFeedback(String theServiceId, Object theServiceBean, Method theMethod) { final CdsFeedbackMethod cdsFeedbackMethod = new CdsFeedbackMethod(theServiceBean, theMethod); myFeedbackMap.put(theServiceId, cdsFeedbackMethod); diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java index 69429a47f96..99c6f314c47 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java @@ -30,6 +30,8 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -49,14 +51,20 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry { private final CdsHooksContextBooter myCdsHooksContextBooter; private final CdsPrefetchSvc myCdsPrefetchSvc; private final ObjectMapper myObjectMapper; + private final ICdsCrServiceFactory myCdsCrServiceFactory; + private final ICrDiscoveryServiceFactory myCrDiscoveryServiceFactory; public CdsServiceRegistryImpl( CdsHooksContextBooter theCdsHooksContextBooter, CdsPrefetchSvc theCdsPrefetchSvc, - ObjectMapper theObjectMapper) { + ObjectMapper theObjectMapper, + ICdsCrServiceFactory theCdsCrServiceFactory, + ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory) { myCdsHooksContextBooter = theCdsHooksContextBooter; myCdsPrefetchSvc = theCdsPrefetchSvc; myObjectMapper = theObjectMapper; + myCdsCrServiceFactory = theCdsCrServiceFactory; + myCrDiscoveryServiceFactory = theCrDiscoveryServiceFactory; } @PostConstruct @@ -142,6 +150,17 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry { theServiceId, theServiceFunction, theCdsServiceJson, theAllowAutoFhirClientPrefetch, theModuleId); } + @Override + public boolean registerCrService(String theServiceId) { + try { + myServiceCache.registerCrService(theServiceId, myCrDiscoveryServiceFactory, myCdsCrServiceFactory); + } catch (Exception e) { + ourLog.error("Error received during CR CDS Service registration: {}", e.getMessage()); + return false; + } + return true; + } + @Override public void unregisterService(String theServiceId, String theModuleId) { Validate.notNull(theServiceId); diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java new file mode 100644 index 00000000000..62615852d76 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java @@ -0,0 +1,48 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +public class CdsCrConstants { + private CdsCrConstants() {} + + public static final String CDS_CR_MODULE_ID = "CR"; + + // CDS Hook field names + public static final String CDS_PARAMETER_USER_ID = "userId"; + public static final String CDS_PARAMETER_PATIENT_ID = "patientId"; + public static final String CDS_PARAMETER_ENCOUNTER_ID = "encounterId"; + public static final String CDS_PARAMETER_MEDICATIONS = "medications"; + public static final String CDS_PARAMETER_PERFORMER = "performer"; + public static final String CDS_PARAMETER_TASK = "task"; + public static final String CDS_PARAMETER_ORDERS = "orders"; + public static final String CDS_PARAMETER_SELECTIONS = "selections"; + public static final String CDS_PARAMETER_DRAFT_ORDERS = "draftOrders"; + public static final String CDS_PARAMETER_APPOINTMENTS = "appointments"; + + // $apply parameter names + public static final String APPLY_PARAMETER_PLAN_DEFINITION = "planDefinition"; + public static final String APPLY_PARAMETER_CANONICAL = "canonical"; + public static final String APPLY_PARAMETER_SUBJECT = "subject"; + public static final String APPLY_PARAMETER_PRACTITIONER = "practitioner"; + public static final String APPLY_PARAMETER_ENCOUNTER = "encounter"; + public static final String APPLY_PARAMETER_PARAMETERS = "parameters"; + public static final String APPLY_PARAMETER_DATA = "data"; + public static final String APPLY_PARAMETER_DATA_ENDPOINT = "dataEndpoint"; +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java new file mode 100644 index 00000000000..17e0ea7afc5 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java @@ -0,0 +1,291 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CarePlan; +import org.hl7.fhir.dstu3.model.Endpoint; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.ParameterDefinition; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.RequestGroup; +import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.dstu3.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.dstu3.Parameters.part; + +public class CdsCrServiceDstu3 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected CarePlan myResponse; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceDstu3(RequestDetails theRequestDetails, Repository theRepository) { + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.DSTU3; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + } + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof CarePlan; + myResponse = (CarePlan) theResponse; + CdsServiceResponseJson serviceResponse = new CdsServiceResponseJson(); + if (myResponse.hasActivity()) { + Reference requestGroupRef = myResponse.getActivity().get(0).getReference(); + RequestGroup mainRequest = (RequestGroup) resolveResource(requestGroupRef); + StringType canonical = mainRequest.getDefinition().get(0).getReferenceElement_(); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> serviceResponse.addCard(resolveAction(action, links))); + } + + return serviceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestGroup.RequestGroupActionComponent theAction, List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected void resolveSystemAction(RequestGroup.RequestGroupActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCode() + && !theAction.getType().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource(RequestGroup.RequestGroupActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getUrl()); + + if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + source.setIcon(documentation.getDocument().getUrl()); + } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion(RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCode() + && !theAction.getType().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + return myResponse.getContained().stream() + .filter(resource -> resource.getId().equals(theReference.getReference())) + .findFirst() + .orElse(null); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java new file mode 100644 index 00000000000..26b0eaa464d --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java @@ -0,0 +1,334 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.ParameterDefinition; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.RequestGroup; +import org.hl7.fhir.r4.model.Resource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.r4.Parameters.part; + +public class CdsCrServiceR4 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected Bundle myResponseBundle; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceR4(RequestDetails theRequestDetails, Repository theRepository) { + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.R4; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + } + endpoint.addHeader("Epic-Client-ID: 2cb5af9f-f483-4e2a-aedc-54c3a31cb153"); + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof Bundle; + myResponseBundle = (Bundle) theResponse; + myServiceResponse = new CdsServiceResponseJson(); + if (myResponseBundle.hasEntry()) { + RequestGroup mainRequest = + (RequestGroup) myResponseBundle.getEntry().get(0).getResource(); + CanonicalType canonical = mainRequest.getInstantiatesCanonical().get(0); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> myServiceResponse.addCard(resolveAction(action, links))); + } + + return myServiceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestGroup.RequestGroupActionComponent theAction, List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasPriority()) { + card.setIndicator(resolveIndicator(theAction.getPriority().toCode())); + } + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected CdsServiceIndicatorEnum resolveIndicator(String theCode) { + CdsServiceIndicatorEnum indicator; + switch (theCode) { + case "routine": + indicator = CdsServiceIndicatorEnum.INFO; + break; + case "urgent": + indicator = CdsServiceIndicatorEnum.WARNING; + break; + case "stat": + indicator = CdsServiceIndicatorEnum.CRITICAL; + break; + default: + indicator = null; + break; + } + if (indicator == null) { + // Code 2435-2440 are reserved for this error message across versions + throw new IllegalArgumentException(Msg.code(2435) + "Invalid priority code: " + theCode); + } + + return indicator; + } + + protected void resolveSystemAction(RequestGroup.RequestGroupActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCodingFirstRep().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource(RequestGroup.RequestGroupActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getUrl()); + + if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + source.setIcon(documentation.getDocument().getUrl()); + } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion(RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCodingFirstRep().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + // Leaving this out until the spec details how to map system actions. + // if (!suggestionAction.getType().isEmpty()) { + // resolveSystemAction(theAction); + // } + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + String reference = theReference.getReference(); + String[] split = reference.split("/"); + String id = reference.contains("/") ? split[1] : reference; + String resourceType = reference.contains("/") ? split[0] : theReference.getType(); + List results = myResponseBundle.getEntry().stream() + .filter(entry -> entry.hasResource() + && entry.getResource().getResourceType().toString().equals(resourceType) + && entry.getResource().getIdPart().equals(id)) + .map(entry -> entry.getResource()) + .collect(Collectors.toList()); + return results.isEmpty() ? null : results.get(0); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java new file mode 100644 index 00000000000..79055a56e1d --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java @@ -0,0 +1,337 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Endpoint; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.ParameterDefinition; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.RequestOrchestration; +import org.hl7.fhir.r5.model.Resource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.r5.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.r5.Parameters.part; + +public class CdsCrServiceR5 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected Bundle myResponseBundle; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceR5(RequestDetails theRequestDetails, Repository theRepository) { + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.R5; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + } + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof Bundle; + myResponseBundle = (Bundle) theResponse; + CdsServiceResponseJson serviceResponse = new CdsServiceResponseJson(); + if (myResponseBundle.hasEntry()) { + RequestOrchestration mainRequest = + (RequestOrchestration) myResponseBundle.getEntry().get(0).getResource(); + CanonicalType canonical = mainRequest.getInstantiatesCanonical().get(0); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> serviceResponse.addCard(resolveAction(action, links))); + } + + return serviceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getDocument().getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestOrchestration.RequestOrchestrationActionComponent theAction, + List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasPriority()) { + card.setIndicator(resolveIndicator(theAction.getPriority().toCode())); + } + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected CdsServiceIndicatorEnum resolveIndicator(String theCode) { + CdsServiceIndicatorEnum indicator; + switch (theCode) { + case "routine": + indicator = CdsServiceIndicatorEnum.INFO; + break; + case "urgent": + indicator = CdsServiceIndicatorEnum.WARNING; + break; + case "stat": + indicator = CdsServiceIndicatorEnum.CRITICAL; + break; + default: + indicator = null; + break; + } + if (indicator == null) { + // Code 2435-2440 are reserved for this error message across versions + throw new IllegalArgumentException(Msg.code(2436) + "Invalid priority code: " + theCode); + } + + return indicator; + } + + protected void resolveSystemAction(RequestOrchestration.RequestOrchestrationActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCodingFirstRep().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getDocument().getUrl()); + + // If we use the document for the url, what do we use for the icon? + // if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + // source.setIcon(documentation.getDocument().getUrl()); + // } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCodingFirstRep().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + // Leaving this out until the spec details how to map system actions. + // if (!suggestionAction.getType().isEmpty()) { + // resolveSystemAction(theAction); + // } + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + String reference = theReference.getReference(); + String[] split = reference.split("/"); + String id = reference.contains("/") ? split[1] : reference; + String resourceType = reference.contains("/") ? split[0] : theReference.getType(); + List results = myResponseBundle.getEntry().stream() + .filter(entry -> entry.hasResource() + && entry.getResource().getResourceType().toString().equals(resourceType) + && entry.getResource().getIdPart().equals(id)) + .map(entry -> entry.getResource()) + .collect(Collectors.toList()); + return results.isEmpty() ? null : results.get(0); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java new file mode 100644 index 00000000000..5f57c5dab31 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java @@ -0,0 +1,41 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; + +public class CdsCrUtils { + public static IBaseResource readPlanDefinitionFromRepository( + FhirVersionEnum theFhirVersion, Repository theRepository, IIdType theId) { + switch (theFhirVersion) { + case DSTU3: + return theRepository.read(org.hl7.fhir.dstu3.model.PlanDefinition.class, theId); + case R4: + return theRepository.read(org.hl7.fhir.r4.model.PlanDefinition.class, theId); + case R5: + return theRepository.read(org.hl7.fhir.r5.model.PlanDefinition.class, theId); + default: + return null; + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java new file mode 100644 index 00000000000..0e393c24e43 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java @@ -0,0 +1,93 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.jpa.cache.IResourceChangeEvent; +import ca.uhn.fhir.jpa.cache.IResourceChangeListener; +import ca.uhn.fhir.jpa.cache.ResourceChangeEvent; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; +import org.hl7.fhir.instance.model.api.IIdType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_CR_MODULE_ID; + +public class CdsServiceInterceptor implements IResourceChangeListener { + static final Logger ourLog = LoggerFactory.getLogger(CdsServiceInterceptor.class); + + @Autowired + CdsServiceRegistryImpl myCdsServiceRegistry; + + public CdsServiceInterceptor() {} + + @Override + public void handleInit(Collection theResourceIds) { + handleChange(ResourceChangeEvent.fromCreatedUpdatedDeletedResourceIds( + new ArrayList<>(theResourceIds), Collections.emptyList(), Collections.emptyList())); + } + + @Override + public void handleChange(IResourceChangeEvent theResourceChangeEvent) { + if (theResourceChangeEvent == null) return; + if (theResourceChangeEvent.getCreatedResourceIds() != null + && !theResourceChangeEvent.getCreatedResourceIds().isEmpty()) { + insert(theResourceChangeEvent.getCreatedResourceIds()); + } + if (theResourceChangeEvent.getUpdatedResourceIds() != null + && !theResourceChangeEvent.getUpdatedResourceIds().isEmpty()) { + update(theResourceChangeEvent.getUpdatedResourceIds()); + } + if (theResourceChangeEvent.getDeletedResourceIds() != null + && !theResourceChangeEvent.getDeletedResourceIds().isEmpty()) { + delete(theResourceChangeEvent.getDeletedResourceIds()); + } + } + + private void insert(List theCreatedIds) { + for (IIdType id : theCreatedIds) { + try { + myCdsServiceRegistry.registerCrService(id.getIdPart()); + } catch (Exception e) { + ourLog.info(String.format("Failed to create service for %s", id.getIdPart())); + } + } + } + + private void update(List updatedIds) { + try { + delete(updatedIds); + insert(updatedIds); + } catch (Exception e) { + ourLog.info(String.format("Failed to update service(s) for %s", updatedIds)); + } + } + + private void delete(List deletedIds) { + for (IIdType id : deletedIds) { + myCdsServiceRegistry.unregisterService(id.getIdPart(), CDS_CR_MODULE_ID); + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java new file mode 100644 index 00000000000..0e14d092958 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IModelJson; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.api.Repository; + +import java.util.Collections; + +public interface ICdsCrService { + IBaseParameters encodeParams(CdsServiceRequestJson theJson); + + CdsServiceResponseJson encodeResponse(Object theResponse); + + FhirVersionEnum getFhirVersion(); + + Repository getRepository(); + + default Object invoke(IModelJson theJson) { + IBaseParameters params = encodeParams((CdsServiceRequestJson) theJson); + IBaseResource response = invokeApply(params); + return encodeResponse(response); + } + + default IBaseResource invokeApply(IBaseParameters theParams) { + var operationName = getFhirVersion() == FhirVersionEnum.R4 + ? ProviderConstants.CR_OPERATION_R5_APPLY + : ProviderConstants.CR_OPERATION_APPLY; + switch (getFhirVersion()) { + case DSTU3: + return getRepository() + .invoke( + org.hl7.fhir.dstu3.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.dstu3.model.CarePlan.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + case R4: + return getRepository() + .invoke( + org.hl7.fhir.r4.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.r4.model.Bundle.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + case R5: + return getRepository() + .invoke( + org.hl7.fhir.r5.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.r5.model.Bundle.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + default: + return null; + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java new file mode 100644 index 00000000000..a17d8efe2db --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java @@ -0,0 +1,24 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +public interface ICdsCrServiceFactory { + ICdsCrService create(String theServiceId); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java new file mode 100644 index 00000000000..3e847e18eb2 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.r4.model.TriggerDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementDstu3 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementDstu3(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTriggerDefinition())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTriggerDefinition()) + .flatMap(a -> a.getTriggerDefinition().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getEventName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java new file mode 100644 index 00000000000..b028001a8c8 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.TriggerDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementR4 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementR4(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTrigger()) + .flatMap(a -> a.getTrigger().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.NONE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.NONE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java new file mode 100644 index 00000000000..bdfbd98e531 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.r4.model.TriggerDefinition; +import org.hl7.fhir.r5.model.PlanDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementR5 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementR5(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTrigger()) + .flatMap(a -> a.getTrigger().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java new file mode 100644 index 00000000000..b71649340b0 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java @@ -0,0 +1,445 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.DataRequirement; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.dstu3.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceDstu3(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.DSTU3, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementDstu3(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition thePlanDefinition) { + if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) { + for (Coding coding : thePlanDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // Assuming 1 library + // TODO: enhance to handle multiple libraries - need a way to identify primary + // library + Library library = null; + if (thePlanDefinition.hasLibrary() + && thePlanDefinition.getLibraryFirstRep().hasReference()) { + library = myRepository.read( + Library.class, thePlanDefinition.getLibraryFirstRep().getReferenceElement()); + } + return library; + } + + public List resolveValueCodingCodes(List theValueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : theValueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(StringType theValueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder( + List theList, StringBuilder theCodes, String theSystem, String theCode) { + String codeToken = theSystem + "|" + theCode; + int postAppendLength = theCodes.length() + codeToken.length(); + + if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) { + theCodes.append(","); + } else if (postAppendLength > myMaxUriLength) { + theList.add(theCodes.toString()); + theCodes = new StringBuilder(); + } + theCodes.append(codeToken); + return theCodes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath()); + + StringType codeFilterComponentString = null; + if (codeFilterComponent.hasValueSetStringType()) { + codeFilterComponentString = codeFilterComponent.getValueSetStringType(); + } else if (codeFilterComponent.hasValueSetReference()) { + codeFilterComponentString = new StringType( + codeFilterComponent.getValueSetReference().getReference()); + } else if (codeFilterComponent.hasValueCoding()) { + List codeFilterValueCodings = codeFilterComponent.getValueCoding(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + + if (codeFilterComponentString != null) { + for (String codes : resolveValueSetCodes(codeFilterComponentString)) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (!library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + case "ProcedureRequest": + if (thePath.equals("bodySite")) return "body-site"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodySite": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "EligibilityRequest": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingManifest": + case "ImagingStudy": + case "Immunization": + case "ImmunizationRecommendation": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "ProcedureRequest": + case "Provenance": + case "QuestionnaireResponse": + case "ReferralRequest": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodySite": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "patient"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "EligibilityRequest": + return "patient"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingManifest": + return "patient"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "ProcedureRequest": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "ReferralRequest": + return "patient"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java new file mode 100644 index 00000000000..0f58472b344 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java @@ -0,0 +1,429 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DataRequirement; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.ValueSet; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.r4.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceR4 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected final Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceR4(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.R4, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementR4(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition planDefinition) { + if (planDefinition.hasType() && planDefinition.getType().hasCoding()) { + for (Coding coding : planDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // The CPGComputablePlanDefinition profile limits the cardinality of library to 1 + Library library = null; + if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) { + library = (Library) SearchHelper.searchRepositoryByCanonical( + myRepository, thePlanDefinition.getLibrary().get(0)); + } + return library; + } + + public List resolveValueCodingCodes(List valueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : valueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(CanonicalType valueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, valueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder(List ret, StringBuilder codes, String system, String code) { + String codeToken = system + "|" + code; + int postAppendLength = codes.length() + codeToken.length(); + + if (codes.length() > 0 && postAppendLength < myMaxUriLength) { + codes.append(","); + } else if (postAppendLength > myMaxUriLength) { + ret.add(codes.toString()); + codes = new StringBuilder(); + } + codes.append(codeToken); + return codes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath()); + if (codeFilterComponent.hasValueSetElement()) { + for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } else if (codeFilterComponent.hasCode()) { + List codeFilterValueCodings = codeFilterComponent.getCode(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (library == null || !library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodyStructure": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "CoverageEligibilityRequest": + case "CoverageEligibilityResponse": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "DocumentReference": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingStudy": + case "Immunization": + case "ImmunizationEvaluation": + case "ImmunizationRecommendation": + case "Invoice": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "MolecularSequence": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "Provenance": + case "QuestionnaireResponse": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "ServiceRequest": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodyStructure": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "policy-holder"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "Invoice": + return "subject"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "MolecularSequence": + return "patient"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "ServiceRequest": + return "patient"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java new file mode 100644 index 00000000000..fb9bd78e9e8 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java @@ -0,0 +1,431 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataRequirement; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.ValueSet; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.r5.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceR5 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected final Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceR5(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.R5, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementR5(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition thePlanDefinition) { + if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) { + for (Coding coding : thePlanDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // The CPGComputablePlanDefinition profile limits the cardinality of library to 1 + Library library = null; + if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) { + library = (Library) SearchHelper.searchRepositoryByCanonical( + myRepository, thePlanDefinition.getLibrary().get(0)); + } + return library; + } + + public List resolveValueCodingCodes(List theValueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : theValueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(CanonicalType theValueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder( + List theList, StringBuilder theCodes, String theSystem, String theCode) { + String codeToken = theSystem + "|" + theCode; + int postAppendLength = theCodes.length() + codeToken.length(); + + if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) { + theCodes.append(","); + } else if (postAppendLength > myMaxUriLength) { + theList.add(theCodes.toString()); + theCodes = new StringBuilder(); + } + theCodes.append(codeToken); + return theCodes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType().toCode())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType().toCode()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = + mapCodePathToSearchParam(theDataRequirement.getType().toCode(), codeFilterComponent.getPath()); + if (codeFilterComponent.hasValueSetElement()) { + for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } else if (codeFilterComponent.hasCode()) { + List codeFilterValueCodings = codeFilterComponent.getCode(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (library == null || !library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodyStructure": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "CoverageEligibilityRequest": + case "CoverageEligibilityResponse": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "DocumentReference": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingStudy": + case "Immunization": + case "ImmunizationEvaluation": + case "ImmunizationRecommendation": + case "Invoice": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "MolecularSequence": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "Provenance": + case "QuestionnaireResponse": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "ServiceRequest": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodyStructure": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "policy-holder"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "Invoice": + return "subject"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "MolecularSequence": + return "patient"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "ServiceRequest": + return "patient"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java new file mode 100644 index 00000000000..f15792ea08d --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java @@ -0,0 +1,30 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; + +public interface ICrDiscoveryElement { + CdsServiceJson getCdsServiceJson(); + + default String getKey(int itemNo) { + return "item" + Integer.toString(itemNo); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java new file mode 100644 index 00000000000..b75a4a2e958 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java @@ -0,0 +1,26 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; + +public interface ICrDiscoveryService { + CdsServiceJson resolveService(); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java new file mode 100644 index 00000000000..250f8db9f0f --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java @@ -0,0 +1,24 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +public interface ICrDiscoveryServiceFactory { + ICrDiscoveryService create(String theServiceId); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java new file mode 100644 index 00000000000..95820cf610c --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; + +public class PrefetchUrlList extends CopyOnWriteArrayList { + + @Override + public boolean add(String theElement) { + for (String s : this) { + if (s.equals(theElement)) return false; + if (theElement.startsWith(s)) return false; + } + return super.add(theElement); + } + + @Override + public boolean addAll(Collection theAdd) { + if (theAdd != null) { + for (String s : theAdd) { + add(s); + } + } + return true; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java index 967da77b47e..81d1cc36a4c 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java @@ -27,6 +27,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceMethod; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsCrServiceMethod; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,6 +57,11 @@ public class CdsPrefetchSvc { public void augmentRequest(CdsServiceRequestJson theCdsServiceRequestJson, ICdsServiceMethod theServiceMethod) { CdsServiceJson serviceSpec = theServiceMethod.getCdsServiceJson(); + if (theServiceMethod instanceof CdsCrServiceMethod) { + // CdsCrServices will retrieve data from the dao or fhir server passed in as needed, + // checking for missing prefetch is not necessary. + return; + } Set missingPrefetch = findMissingPrefetch(serviceSpec, theCdsServiceRequestJson); if (missingPrefetch.isEmpty()) { return; diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java new file mode 100644 index 00000000000..ca66a5636a1 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java @@ -0,0 +1,17 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirContext; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {TestCrConfig.class}) +public abstract class BaseCrTest { + public static final String PLAN_DEFINITION_RESOURCE_NAME = "PlanDefinition"; + protected static final String TEST_ADDRESS = "http://test:8000/fhir"; + + @Autowired + protected FhirContext myFhirContext; +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java new file mode 100644 index 00000000000..05e0201284b --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java @@ -0,0 +1,14 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestCrConfig { + @Bean + FhirContext fhirContext() { + return FhirContext.forR4Cached(); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java new file mode 100644 index 00000000000..e8b5eee22aa --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java @@ -0,0 +1,44 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.IdType; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CrDiscoveryServiceR4Test extends BaseCrTest { + @Test + public void testR4DiscoveryService() throws JsonProcessingException { + Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + final CdsServiceJson cdsServiceJson = new CrDiscoveryServiceR4(planDefinitionId, repository).resolveService(); + final ObjectMapper objectMapper = new CdsHooksObjectMapperFactory(myFhirContext).newMapper(); + // execute + final String actual = objectMapper.writeValueAsString(cdsServiceJson); + final String expected = "{\n" + + " \"hook\" : \"order-sign\",\n" + + " \"title\" : \"ASLPCrd Workflow\",\n" + + " \"description\" : \"An example workflow for the CRD step of DaVinci Burden Reduction.\",\n" + + " \"id\" : \"ASLPCrd\",\n" + + " \"prefetch\" : {\n" + + " \"item1\" : \"Patient?_id=Patient/{{context.patientId}}\",\n" + + " \"item2\" : \"ServiceRequest?patient=Patient/{{context.patientId}}\",\n" + + " \"item3\" : \"Condition?patient=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE19\",\n" + + " \"item4\" : \"Condition?patient=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE18\",\n" + + " \"item5\" : \"Observation?subject=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE19\"\n" + + " }\n" + + "}"; + assertEquals(expected, actual); + } + +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java new file mode 100644 index 00000000000..e0179374882 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java @@ -0,0 +1,78 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.resolution; + +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.codesystems.ActionType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CdsCrServiceR4Test extends BaseCrTest { + private ObjectMapper myObjectMapper;@BeforeEach + public void loadJson() throws IOException { + myObjectMapper = new CdsHooksObjectMapperFactory(myFhirContext).newMapper(); + } + + @Test + public void testR4Params() throws IOException { + final String rawRequest = ClasspathUtil.loadResource("ASLPCrdServiceRequest.json"); + final CdsServiceRequestJson cdsServiceRequestJson = myObjectMapper.readValue(rawRequest, CdsServiceRequestJson.class); + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + requestDetails.setId(planDefinitionId); + final Parameters params = new CdsCrServiceR4(requestDetails, repository).encodeParams(cdsServiceRequestJson); + + assertTrue(params.getParameter().size() == 3); + assertTrue(params.getParameter("parameters").hasResource()); + } + + @Test + public void testR4Response() { + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final Bundle responseBundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Response.json"); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + requestDetails.setId(planDefinitionId); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository).encodeResponse(responseBundle); + + assertTrue(cdsServiceResponseJson.getCards().size() == 1); + assertTrue(!cdsServiceResponseJson.getCards().get(0).getSummary().isEmpty()); + assertTrue(!cdsServiceResponseJson.getCards().get(0).getDetail().isEmpty()); + } + + @Test + @Disabled // Disabled until the CDS on FHIR specification details how to map system actions. + public void testSystemActionResponse() { + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-DischargeInstructionsPlan-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final Bundle responseBundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-DischargeInstructionsPlan-Response.json"); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "DischargeInstructionsPlan"); + requestDetails.setId(planDefinitionId); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository).encodeResponse(responseBundle); + + assertTrue(cdsServiceResponseJson.getServiceActions().size() == 1); + assertTrue(cdsServiceResponseJson.getServiceActions().get(0).getType().equals(ActionType.CREATE.toCode())); + assertNotNull(cdsServiceResponseJson.getServiceActions().get(0).getResource()); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json new file mode 100644 index 00000000000..474e666e302 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json @@ -0,0 +1,87 @@ +{ + "hook" : "order-sign", + "hookInstance": "randomGUIDforthehookevent", + "fhirServer" : "https://localhost:8000", + "context" : { + "patientId" : "Patient/123", + "draftOrders" : { + "resourceType": "Bundle", + "entry": [ + { + "resource": { + "resourceType": "ServiceRequest", + "id": "SleepStudy", + "meta": { + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest" + ] + }, + "status": "draft", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + ], + "text": "Home sleep apnea testing (HSAT)" + }, + "subject": { + "reference": "Patient/positive" + }, + "authoredOn": "2023-04-06", + "reasonReference": [ + { + "reference": "Condition/SleepApnea" + } + ], + "occurrenceDateTime": "2023-04-10T08:00:00.000Z", + "requester": { + "reference": "Practitioner/Practitioner-positive" + } + } + }, + { + "resource": { + "resourceType": "ServiceRequest", + "id": "SleepStudy2", + "meta": { + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest" + ] + }, + "status": "draft", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + ], + "text": "Artificial intelligence (AI)" + }, + "subject": { + "reference": "Patient/positive" + }, + "authoredOn": "2023-04-06", + "reasonReference": [ + { + "reference": "Condition/SleepApnea" + } + ], + "occurrenceDateTime": "2023-04-15T08:00:00.000Z", + "requester": { + "reference": "Practitioner/Practitioner-positive" + } + } + } + ] + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json new file mode 100644 index 00000000000..9b99b5f794c --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json @@ -0,0 +1,1450 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ASLPCrd", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://example.org/sdh/dtr/aslp/Library/ASLPCrd", + "name": "ASLPCrd", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library Cx", + "resource": "http://example.org/sdh/dtr/aslp/Library/ASLPConcepts" + }, + { + "type": "depends-on", + "display": "Library Dx", + "resource": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://example.org/sdh/dtr/aslp/Library/FHIRHelpers|4.1.000" + }, + { + "type": "depends-on", + "display": "Library SC", + "resource": "http://example.org/sdh/dtr/aslp/Library/SDHCommon" + }, + { + "type": "depends-on", + "display": "Library FC", + "resource": "http://example.org/sdh/dtr/aslp/Library/FHIRCommon|1.1.000" + }, + { + "type": "depends-on", + "display": "Code system ASLP Codes", + "resource": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Value set Home Based Testing Sleep Studies Codes", + "resource": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de2" + }, + { + "type": "depends-on", + "display": "Value set Active Condition", + "resource": "http://fhir.org/guides/cqf/common/ValueSet/active-condition" + } + ], + "parameter": [ + { + "name": "Service Request Id", + "use": "in", + "min": 0, + "max": "*", + "type": "string" + }, + { + "name": "Service Request", + "use": "in", + "min": 0, + "max": "*", + "type": "ServiceRequest" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "Is Sleep Study Service Request", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Has Comorbidities", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Is Prior Auth Required", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Get Card Summary", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Rationale", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Get Card Detail", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Get Card Indicator", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "string", + "profile": [ + "http://hl7.org/fhir/string" + ], + "mustSupport": [ + "value" + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "id" + ] + }, + { + "profile": [ + "http://hl7.org/fhir/ObservationStatus" + ], + "mustSupport": [ + "value" + ] + }, + { + "type": "Quantity", + "profile": [ + "http://hl7.org/fhir/Quantity" + ], + "mustSupport": [ + "value", + "comparator", + "system", + "system.value", + "value.value", + "code", + "code.value", + "unit", + "unit.value" + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code", + "clinicalStatus", + "verificationStatus" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE19", + "display": "History of Diabetes" + } + ] + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code", + "clinicalStatus", + "verificationStatus" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE18", + "display": "History of Hypertension" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "code", + "status", + "value" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE19", + "display": "History of Diabetes" + } + ] + } + ] + }, + { + "type": "Quantity", + "profile": [ + "urn:hl7-org:elm-types:r1/Quantity" + ], + "mustSupport": [ + "value" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBBU0xQQ3JkCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBBU0xQQ29uY2VwdHMgY2FsbGVkIEN4CmluY2x1ZGUgQVNMUERhdGFFbGVtZW50cyBjYWxsZWQgRHgKaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjEuMDAwJwoKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIklzIFByaW9yIEF1dGggUmVxdWlyZWQiOgogICAgIklzIFNsZWVwIFN0dWR5IFNlcnZpY2UgUmVxdWVzdCIgYW5kICJIYXMgQ29tb3JiaWRpdGllcyIKCmRlZmluZSAiSGFzIENvbW9yYmlkaXRpZXMiOgogRHguIkhpc3Rvcnkgb2YgRGlhYmV0ZXMiIG9yIER4LiJIaXN0b3J5IG9mIEh5cGVydGVuc2lvbiIKCmRlZmluZSAiSXMgU2xlZXAgU3R1ZHkgU2VydmljZSBSZXF1ZXN0IjoKICAgIGV4aXN0cyAoRHguIlNsZWVwIFN0dWR5IiBTIHdoZXJlIFMuY29kZSBpbiBDeC4iSG9tZSBCYXNlZCBUZXN0aW5nIFNsZWVwIFN0dWRpZXMgQ29kZXMiKQoKZGVmaW5lICJHZXQgQ2FyZCBTdW1tYXJ5IjoKICBpZiAiSXMgUHJpb3IgQXV0aCBSZXF1aXJlZCIgdGhlbgogICAgJ1BhdGllbnQgcmVxdWlyZXMgcHJpb3IgYXV0aG9yaXp0aW9uIGZvciBhIHNsZWVwIHN0dWR5JwogIGVsc2UKICAgICdQYXRpZW50IGRvZXMgbm90IHJlcXVpcmUgcHJpb3IgYXV0aG9yaXphdGlvbiBmb3IgYSBzbGVlcCBzdHVkeScKCmRlZmluZSAiR2V0IENhcmQgRGV0YWlsIjoKICBpZiAiSXMgUHJpb3IgQXV0aCBSZXF1aXJlZCIgdGhlbgogICAgJ1BhdGllbnQgcmVxdWlyZXMgcHJpb3IgYXV0aG9yaXphdGlvbiBkdWUgdG86ICcgKyBSYXRpb25hbGUgKyAnLiAnICsKICAgICdQbGVhc2Ugb3BlbiB5b3VyIERUUiBhcHBsaWNhdGlvbiBhbmQgY29tcGxldGUgUXVlc3Rpb25uaWFyZScKICBlbHNlCiAgICAnUGF0aWVudCBkb2VzIG5vdCByZXF1aXJlIHByaW9yIGF1dGhvcml6YXRpb24uJwoKZGVmaW5lICJSYXRpb25hbGUiOgogIENvYWxlc2NlKHsKICAgIGlmIER4LiJIaXN0b3J5IG9mIERpYWJldGVzIiB0aGVuICdoaXN0b3J5IG9mIGRpYWJldGVzJyBlbHNlIG51bGwsCiAgICBpZiBEeC4iSGlzdG9yeSBvZiBIeXBlcnRlbnNpb24iIHRoZW4gJ2hpc3Rvcnkgb2YgaHlwZXJ0ZW5zaW9uJyBlbHNlIG51bGwsCiAgICAnbm8gcmF0aW9uYWxlIHByb3ZpZGVkJwogIH0pCgoKZGVmaW5lICJHZXQgQ2FyZCBJbmRpY2F0b3IiOgogIGlmICJJcyBQcmlvciBBdXRoIFJlcXVpcmVkIiB0aGVuCiAgICAnd2FybmluZycKICBlbHNlCiAgICAnaW5mbycKCmRlZmluZSAiUXVlc3Rpb25uYWlyZSBJbnB1dCI6CiAgeyB0eXBlOiAnY29sbGVjdC1pbmZvcm1hdGlvbicsIHZhbHVlQ2Fub25pY2FsOiAnaHR0cDovL2V4YW1wbGUub3JnL3NkaC9kdHIvYXNscC9RdWVzdGlvbm5haXJlL0FTTFBBMScgfQoKZGVmaW5lICJRdWVzdGlvbm5haXJlIElucHV0IFR5cGUiOgogICdjb2xsZWN0LWluZm9ybWF0aW9uJwoKZGVmaW5lICJRdWVzdGlvbm5haXJlIElucHV0IENhbm9uaWNhbCI6CiAgJ2h0dHA6Ly9leGFtcGxlLm9yZy9zZGgvZHRyL2FzbHAvUXVlc3Rpb25uYWlyZS9BU0xQQTEnCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "ActivityDefinition", + "id": "ASLPCrd", + "meta": { + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-collectinformationactivity" + }, + "url": "http://example.org/sdh/dtr/aslp/ActivityDefinition/ASLPCrd", + "version": "1.0.0", + "kind": "Task", + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-questionnairetask", + "intent": "proposal", + "priority": "routine", + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "code": "collect-information", + "display": "Collect Information" + } + ] + }, + "library": [ + "http://example.org/sdh/dtr/aslp/Library/ASLPCrd" + ], + "dynamicValue": [ + { + "path": "input[0].type", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Type" + } + }, + { + "path": "input[0].valueCanonical", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Canonical" + } + }, + { + "path": "input[1].type", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Type" + } + }, + { + "path": "input[1].valueCanonical", + "expression": { + "language": "text/cql", + "expression": "'http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA2'" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "ActivityDefinition/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "PlanDefinition", + "id": "ASLPCrd", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-recommendationdefinition" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-questionnaire-generate", + "valueBoolean": true + } + ], + "url": "http://example.org/sdh/dtr/aslp/PlanDefinition/ASLPCrd", + "identifier": [ + { + "use": "official", + "value": "generate-questionnaire-sample" + } + ], + "version": "1.0.0", + "name": "ASLPCrd", + "title": "ASLPCrd Workflow", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", + "code": "eca-rule", + "display": "ECA Rule" + } + ] + }, + "status": "draft", + "experimental": true, + "description": "An example workflow for the CRD step of DaVinci Burden Reduction.", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "jurisdiction": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/iso3166-1-3", + "version": "4.0.1", + "code": "USA", + "display": "United States of America" + } + ] + } + ], + "purpose": "The purpose of this is to test the system to make sure we have complete end-to-end functionality", + "usage": "This is to be used in conjunction with a patient-facing FHIR application.", + "relatedArtifact": [ + { + "type": "depends-on", + "resource": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + } + ], + "library": [ + "http://example.org/sdh/dtr/aslp/Library/ASLPCrd" + ], + "action": [ + { + "trigger": [ + { + "type": "named-event", + "name": "order-sign" + } + ], + "extension": [], + "title": "Does order require PriorAuth?", + "description": "", + "condition": [ + { + "kind": "applicability", + "expression": { + "language": "text/cql-identifier", + "expression": "Is Prior Auth Required" + } + } + ], + "input": [ + { + "type": "ServiceRequest", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + ] + } + ], + "definitionCanonical": "http://example.org/sdh/dtr/aslp/ActivityDefinition/ASLPCrd", + "dynamicValue": [ + { + "path": "action.title", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Summary" + } + }, + { + "path": "action.description", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Detail" + } + }, + { + "path": "action.extension", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Indicator" + } + } + ], + "action": [ + { + "input": [ + { + "type": "Condition", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi" + ] + } + ] + } + ] + } + ] + }, + "request": { + "method": "PUT", + "url": "PlanDefinition/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-bmi", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi", + "name": "ASLPBMI", + "title": "ASLP BMI", + "status": "draft", + "experimental": false, + "description": "ASLP BMI", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "BMI", + "definition": "Body mass index (BMI)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE22" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "BMI", + "definition": "Body mass index (BMI)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE22" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-bmi" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-diagnosis-of-obstructive-sleep-apnea", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea", + "name": "ASLPDiagnosisofObstructiveSleepApnea", + "title": "ASLP Diagnosis of Obstructive Sleep Apnea", + "status": "draft", + "experimental": false, + "description": "ASLP Diagnosis of Obstructive Sleep Apnea", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Condition", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Condition", + "path": "Condition", + "mustSupport": false + }, + { + "id": "Condition.code", + "path": "Condition.code", + "short": "Diagnosis of Obstructive Sleep Apnea", + "definition": "Diagnosis of Obstructive Sleep Apnea", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Diagnosis of Obstructive Sleep Apnea Codes" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de17" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE16" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-height", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height", + "name": "ASLPHeight", + "title": "ASLP Height", + "status": "draft", + "experimental": false, + "description": "ASLP Height", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Height", + "definition": "Height (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Height", + "definition": "Height (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-height" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-history-of-diabetes", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes", + "name": "ASLPHistoryofDiabetes", + "title": "ASLP History of Diabetes", + "status": "draft", + "experimental": false, + "description": "ASLP History of Diabetes", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "History of Diabetes", + "definition": "History of Diabetes", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE19" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "History of Diabetes", + "definition": "History of Diabetes", + "min": 1, + "max": "1", + "type": [ + { + "code": "boolean" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE19" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-history-of-diabetes" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-history-of-hypertension", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension", + "name": "ASLPHistoryofHypertension", + "title": "ASLP History of Hypertension", + "status": "draft", + "experimental": false, + "description": "ASLP History of Hypertension", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "History of Hypertension", + "definition": "History of Hypertension", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE18" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "History of Hypertension", + "definition": "History of Hypertension", + "min": 1, + "max": "1", + "type": [ + { + "code": "boolean" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE18" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-history-of-hypertension" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-neck-circumference", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference", + "name": "ASLPNeckCircumference", + "title": "ASLP Neck Circumference", + "status": "draft", + "experimental": false, + "description": "ASLP Neck Circumference", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Neck Circumference", + "definition": "Neck circumference (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Neck Circumference", + "definition": "Neck circumference (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-neck-circumference" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-servicerequest", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-servicerequest", + "name": "ASLPServiceRequest", + "title": "ASLP ServiceRequest", + "status": "draft", + "experimental": false, + "description": "ASLP ServiceRequest", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "ServiceRequest", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "ServiceRequest", + "path": "ServiceRequest", + "mustSupport": false + }, + { + "id": "ServiceRequest.code", + "path": "ServiceRequest.code", + "short": "Procedure Code", + "definition": "The procedures being approved", + "comment": "The procedures for which approval is being requested", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Procedure Code Codes Grouper" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de1-codes-grouper" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE1" + } + ] + }, + { + "id": "ServiceRequest.occurrence[x]", + "path": "ServiceRequest.occurrence[x]", + "short": "Procedure Date", + "definition": "Date of the procedure", + "min": 1, + "max": "1", + "type": [ + { + "code": "dateTime" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ALSP.A1.DE15" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-servicerequest" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-sleep-study-order", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "name": "ASLPSleepStudyOrder", + "title": "ASLP Sleep Study Order", + "status": "draft", + "experimental": false, + "description": "ASLP Sleep Study Order", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "ServiceRequest", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "ServiceRequest", + "path": "ServiceRequest", + "mustSupport": false + }, + { + "id": "ServiceRequest.code", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Sleep Study Code", + "reference": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + } + } + ], + "path": "ServiceRequest.code", + "short": "Sleep Study", + "definition": "A sleep study procedure being ordered", + "comment": "The procedures for which approval is being requested", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Sleep Study Codes Grouper" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de1-codes-grouper" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE1" + } + ] + }, + { + "id": "ServiceRequest.occurrence[x]", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Sleep Study Date", + "reference": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + } + } + ], + "path": "ServiceRequest.occurrence[x]", + "short": "Sleep Study Date", + "definition": "Date of the procedure", + "min": 1, + "max": "1", + "type": [ + { + "code": "dateTime" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ALSP.A1.DE15" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-sleep-study-order" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-weight", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight", + "name": "ASLPWeight", + "title": "ASLP Weight", + "status": "draft", + "experimental": false, + "description": "ASLP Weight", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Weight", + "definition": "Weight (in pounds)", + "type": [ + { + "code": "CodeableConcept" + } + ], + "patternCodeableConcept": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE21", + "display": "Body weight" + } + ] + }, + "min": 1, + "max": "1", + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE21" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Weight", + "definition": "Weight (in pounds)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE21" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-weight" + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json new file mode 100644 index 00000000000..3994ba29beb --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json @@ -0,0 +1,388 @@ +{ + "resourceType": "Bundle", + "id": "ASLPCrd", + "type": "collection", + "entry": [ + { + "resource": { + "resourceType": "RequestGroup", + "id": "ASLPCrd", + "instantiatesCanonical": [ + "http://example.org/sdh/dtr/aslp/PlanDefinition/ASLPCrd|1.0.0" + ], + "status": "draft", + "intent": "proposal", + "subject": { + "reference": "positive" + }, + "action": [ + { + "extension": [ + { + "url": null, + "valueString": "warning" + } + ], + "title": "Patient requires prior authoriztion for a sleep study", + "description": "Patient requires prior authorization due to: history of diabetes. Please open your DTR application and complete Questionniare", + "resource": { + "reference": "Task/ASLPCrd" + } + } + ] + } + }, + { + "resource": { + "resourceType": "Task", + "id": "ASLPCrd", + "meta": { + "versionId": "1" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/aphl/StructureDefinition/condition", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Is Prior Auth Required" + } + }, + { + "url": "http://hl7.org/fhir/aphl/StructureDefinition/input", + "valueDataRequirement": { + "type": "ServiceRequest", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + ] + } + } + ], + "basedOn": [ + { + "reference": "RequestGroup/ASLPCrd", + "type": "RequestGroup" + } + ], + "status": "draft", + "intent": "proposal", + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "code": "collect-information", + "display": "Collect Information" + } + ] + }, + "for": { + "reference": "positive" + }, + "input": [ + { + "type": { + "coding": [ + { + "code": "collect-information" + } + ] + }, + "valueCanonical": "http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA1" + }, + { + "type": { + "coding": [ + { + "code": "collect-information" + } + ] + }, + "valueCanonical": "http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA2" + } + ] + } + }, + { + "resource": { + "resourceType": "Questionnaire", + "id": "ASLPCrd", + "item": [ + { + "linkId": "1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "text": "ASLP Sleep Study Order", + "type": "group", + "item": [ + { + "linkId": "1.1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order#ServiceRequest.code", + "text": "Sleep Study", + "type": "choice", + "required": true, + "answerOption": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE3", + "display": "Peripheral artery tonometry (PAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE4", + "display": "Actigraphy" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE5", + "display": "Prescreening devices or procedures" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE6", + "display": "Acoustic pharyngometry" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE7", + "display": "Digital therapeutics" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE8", + "display": "Home oximetry monitoring" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE9", + "display": "Polysomnogram" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE10", + "display": "Facility-based positive airway pressure (PAP) titration study" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE11", + "display": "Facility-based, daytime, abbreviated, cardiorespiratory sleep studies (PAP NAP testing)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE12", + "display": "Multiple sleep latency test (MSLT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE13", + "display": "Maintenance of wakefulness test (MWT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + } + ], + "initial": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + } + ] + }, + { + "linkId": "1.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order#ServiceRequest.occurrence[x]", + "text": "Sleep Study Date", + "type": "dateTime", + "required": true, + "initial": [ + { + "valueDateTime": "2023-04-10T08:00:00.000Z" + }, + { + "valueDateTime": "2023-04-15T08:00:00.000Z" + } + ] + } + ] + }, + { + "linkId": "2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea", + "text": "ASLP Diagnosis of Obstructive Sleep Apnea", + "type": "group", + "item": [ + { + "linkId": "2.1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea#Condition.code", + "text": "Diagnosis of Obstructive Sleep Apnea", + "type": "choice", + "required": true, + "answerOption": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE17", + "display": "Obstructive sleep apnea (OSA)" + } + } + ] + } + ] + }, + { + "linkId": "3", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension", + "text": "ASLP History of Hypertension", + "type": "group", + "item": [ + { + "linkId": "3.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "3.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension#Observation.value[x]", + "text": "History of Hypertension", + "type": "boolean", + "required": true + } + ] + }, + { + "linkId": "4", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes", + "text": "ASLP History of Diabetes", + "type": "group", + "item": [ + { + "linkId": "4.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "4.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes#Observation.value[x]", + "text": "History of Diabetes", + "type": "boolean", + "required": true + } + ] + }, + { + "linkId": "5", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference", + "text": "ASLP Neck Circumference", + "type": "group", + "item": [ + { + "linkId": "5.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "5.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "6", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height", + "text": "ASLP Height", + "type": "group", + "item": [ + { + "linkId": "6.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "6.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "7", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight", + "text": "ASLP Weight", + "type": "group", + "item": [ + { + "linkId": "7.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "7.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "8", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi", + "text": "ASLP BMI", + "type": "group", + "item": [ + { + "linkId": "8.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "8.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + } + ] + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json new file mode 100644 index 00000000000..0193c55f157 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json @@ -0,0 +1,161 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "fullUrl": "ActivityDefinition/SendMessageActivity", + "resource": { + "resourceType": "ActivityDefinition", + "id": "SendMessageActivity", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationactivity" + ] + }, + "kind": "CommunicationRequest", + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationrequest", + "intent": "proposal", + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "structured" + } + ], + "url": "http://example.org/ActivityDefinition/SendMessageActivity", + "name": "SendMessageActivity", + "title": "ActivityDefinition SendMessageActivity", + "status": "draft", + "experimental": true, + "publisher": "Example", + "jurisdiction": [ + { + "coding": [ + { + "code": "001", + "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", + "display": "World" + } + ] + } + ], + "version": "0.1.0", + "description": "Example Activity Definition for a recommendation to send a message", + "code": { + "coding": [ + { + "code": "send-message", + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "display": "Send a message" + } + ] + }, + "doNotPerform": false, + "dynamicValue": [ + { + "path": "payload[0].contentString", + "expression": { + "language": "text/fhirpath", + "expression": "'Greeting: Hello! ' + %subject.name.given.first() + ' Message: Example Activity Definition for a recommendation to send a message Practitioner: ' + %practitioner.name.given.first()" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "ActivityDefinition/SendMessageActivity" + } + }, + { + "fullUrl": "PlanDefinition/DischargeInstructionsPlan", + "resource": { + "resourceType": "PlanDefinition", + "id": "DischargeInstructionsPlan", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-computableplandefinition" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "structured" + } + ], + "url": "http://example.org/PlanDefinition/DischargeInstructionsPlan", + "name": "DischargeInstructionsPlan", + "title": "PlanDefinition DischargeInstructionsPlan", + "status": "draft", + "experimental": true, + "publisher": "Example", + "jurisdiction": [ + { + "coding": [ + { + "code": "001", + "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", + "display": "World" + } + ] + } + ], + "version": "0.1.0", + "description": "Provide patient discharge instructions", + "type": { + "coding": [ + { + "code": "clinical-protocol", + "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", + "display": "Clinical Protocol" + } + ] + }, + "action": [ + { + "title": "Send message with discharge instructions", + "code": [ + { + "coding": [ + { + "code": "provide-counseling", + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-common-process", + "display": "Provide Counseling" + } + ] + } + ], + "type": { + "coding": [ + { + "code": "create", + "system": "http://terminology.hl7.org/CodeSystem/action-type" + } + ] + }, + "dynamicValue": [ + { + "path": "payload[0].contentString", + "expression": { + "language": "text/fhirpath", + "expression": "'Provide patient discharge instructions for ' + %subject.name.given.first()" + } + } + ], + "definitionCanonical": "http://example.org/ActivityDefinition/SendMessageActivity" + } + ] + }, + "request": { + "method": "PUT", + "url": "PlanDefinition/DischargeInstructionsPlan" + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json new file mode 100644 index 00000000000..f722c2f511f --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json @@ -0,0 +1,87 @@ +{ + "resourceType": "Bundle", + "id": "DischargeInstructionsPlan", + "type": "collection", + "entry": [ + { + "resource": { + "resourceType": "RequestGroup", + "id": "DischargeInstructionsPlan", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-strategy" + ] + }, + "instantiatesCanonical": [ + "http://example.org/PlanDefinition/DischargeInstructionsPlan|0.1.0" + ], + "status": "draft", + "intent": "proposal", + "subject": { + "reference": "Patient/Patient1" + }, + "encounter": { + "reference": "Encounter/Encounter1" + }, + "author": { + "reference": "Practitioner/Practitioner1" + }, + "action": [ + { + "title": "Send message with discharge instructions", + "code": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-common-process", + "code": "provide-counseling", + "display": "Provide Counseling" + } + ] + } + ], + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/action-type", + "code": "create" + } + ] + }, + "resource": { + "reference": "CommunicationRequest/SendMessageActivity" + } + } + ] + } + }, + { + "resource": { + "resourceType": "CommunicationRequest", + "id": "SendMessageActivity", + "meta": { + "versionId": "2", + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationrequest" + ] + }, + "status": "draft", + "doNotPerform": false, + "subject": { + "reference": "Patient/Patient1" + }, + "encounter": { + "reference": "Encounter/Encounter1" + }, + "payload": [ + { + "contentString": "Provide patient discharge instructions for Alice" + } + ], + "requester": { + "reference": "Practitioner/Practitioner1" + } + } + } + ] +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java index 0dd4f0667ba..b5e25ade7ee 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.ElementsSupportEnum; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableListMultimap; @@ -72,6 +73,7 @@ public class SystemRequestDetails extends RequestDetails { super(theDetails); if (nonNull(theDetails.getServer())) { myServer = theDetails.getServer(); + myFhirContext = theDetails.getFhirContext(); } } @@ -152,6 +154,10 @@ public class SystemRequestDetails extends RequestDetails { return myServer; } + public void setServer(RestfulServer theServer) { + this.myServer = theServer; + } + @Override public String getServerBaseForRequest() { return null; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java new file mode 100644 index 00000000000..2d5863fd027 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java @@ -0,0 +1,74 @@ +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2023 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.api.server; + +import ca.uhn.fhir.rest.server.BaseRestfulResponse; +import ca.uhn.fhir.util.IoUtil; +import org.apache.commons.lang3.Validate; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A default RestfulResponse that returns the body as an IBaseResource and ignores everything else. + */ +public class SystemRestfulResponse extends BaseRestfulResponse { + private Writer myWriter; + private ByteArrayOutputStream myOutputStream; + + public SystemRestfulResponse(SystemRequestDetails theSystemRequestDetails) { + super(theSystemRequestDetails); + } + + @Nonnull + @Override + public Writer getResponseWriter(int theStatusCode, String theContentType, String theCharset, boolean theRespondGzip) + throws IOException { + Validate.isTrue(myWriter == null, "getResponseWriter() called multiple times"); + Validate.isTrue(myOutputStream == null, "getResponseWriter() called after getResponseOutputStream()"); + + myWriter = new StringWriter(); + return myWriter; + } + + @Nonnull + @Override + public OutputStream getResponseOutputStream( + int theStatusCode, String theContentType, @Nullable Integer theContentLength) throws IOException { + Validate.isTrue(myWriter == null, "getResponseOutputStream() called multiple times"); + Validate.isTrue(myOutputStream == null, "getResponseOutputStream() called after getResponseWriter()"); + + myOutputStream = new ByteArrayOutputStream(); + return myOutputStream; + } + + @Override + public Object commitResponse(@Nonnull Closeable theWriterOrOutputStream) throws IOException { + IoUtil.closeQuietly(theWriterOrOutputStream); + + return getRequestDetails().getServer().getFhirContext().newJsonParser().parseResource(myWriter.toString()); + } +} diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index c7b6ee03823..bceff64f591 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -139,6 +139,12 @@ ${spring-security-core.version} + + org.springframework.boot + spring-boot-autoconfigure + ${spring_boot_version} + + javax.servlet diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java index 8504c2e2060..5166a5ebaf6 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java @@ -23,10 +23,12 @@ import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.repo.HapiFhirRepository; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.server.RestfulServer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration +@ConditionalOnBean(RestfulServer.class) public class RepositoryConfig { @Bean IRepositoryFactory repositoryFactory(DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java index 0c6240813a8..d72885a63e6 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java @@ -26,14 +26,17 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class ApplyOperationConfig { - @Bean ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory dstu3ActivityDefinitionProcessorFactory( IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { @@ -61,7 +64,6 @@ public class ApplyOperationConfig { @Bean(name = "applyOperationLoader") public ProviderLoader applyOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java index 3b0fb8c9a93..773e57da71c 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class ExtractOperationConfig { @Bean ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory dstu3QuestionnaireResponseProcessorFactory( @@ -49,7 +53,6 @@ public class ExtractOperationConfig { @Bean(name = "extractOperationLoader") public ProviderLoader extractOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java index 0596e0eb0b7..1617dfdfcbf 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class PackageOperationConfig { @Bean ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( @@ -60,7 +64,6 @@ public class PackageOperationConfig { @Bean(name = "packageOperationLoader") public ProviderLoader packageOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java index a7ce50adae6..bccaab2d578 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class PopulateOperationConfig { @Bean ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( @@ -48,7 +52,6 @@ public class PopulateOperationConfig { @Bean(name = "populateOperationLoader") public ProviderLoader populateOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java index cc21be6340e..89d732b54fd 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java @@ -26,14 +26,17 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class ApplyOperationConfig { - @Bean ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory r4ActivityDefinitionProcessorFactory( IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { @@ -61,7 +64,6 @@ public class ApplyOperationConfig { @Bean(name = "applyOperationLoader") public ProviderLoader applyOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java index a4edeec5d6a..a33f1267742 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class ExtractOperationConfig { @Bean ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory r4QuestionnaireResponseProcessorFactory( @@ -49,7 +53,6 @@ public class ExtractOperationConfig { @Bean(name = "extractOperationLoader") public ProviderLoader extractOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java index 640fdfd0239..7429a0dfe53 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class PackageOperationConfig { @Bean ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( @@ -60,7 +64,6 @@ public class PackageOperationConfig { @Bean(name = "packageOperationLoader") public ProviderLoader packageOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java index 9d604475435..da10bb376f6 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java @@ -26,12 +26,16 @@ import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import java.util.Arrays; import java.util.Map; +@Configuration +@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) public class PopulateOperationConfig { @Bean ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( @@ -48,7 +52,6 @@ public class PopulateOperationConfig { @Bean(name = "populateOperationLoader") public ProviderLoader populateOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java index 6d6e68049d1..31a4a147a0e 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.cr.repo; +import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; @@ -43,6 +44,7 @@ class RequestDetailsCloner { newDetails.setParameters(new HashMap<>()); newDetails.setResourceName(null); newDetails.setCompartmentName(null); + newDetails.setResponse(theDetails.getResponse()); return new DetailsBuilder(newDetails); } @@ -65,7 +67,9 @@ class RequestDetailsCloner { } DetailsBuilder setParameters(IBaseParameters theParameters) { - myDetails.setResource(theParameters); + IParser parser = myDetails.getServer().getFhirContext().newJsonParser(); + myDetails.setRequestContents( + parser.encodeResourceToString(theParameters).getBytes()); return this; } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java index a29d3960493..1d0eeafeeb5 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java @@ -2,6 +2,10 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.cr.IResourceLoader; +import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; +import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig; +import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; +import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; @@ -35,7 +39,13 @@ import java.util.List; import java.util.concurrent.TimeUnit; -@ContextConfiguration(classes = {TestCrR4Config.class}) +@ContextConfiguration(classes = { + TestCrR4Config.class, + ApplyOperationConfig.class, + ExtractOperationConfig.class, + PackageOperationConfig.class, + PopulateOperationConfig.class +}) public abstract class BaseCrR4TestServer extends BaseJpaR4Test implements IResourceLoader { public static IGenericClient ourClient; diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java index 7f4cc6f46c8..d04be9cb71a 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java @@ -31,12 +31,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Configuration -@Import({TestCrConfig.class, CrR4Config.class, - ApplyOperationConfig.class, - ExtractOperationConfig.class, - PackageOperationConfig.class, - PopulateOperationConfig.class -}) +@Import({TestCrConfig.class, CrR4Config.class}) public class TestCrR4Config { @Primary @Bean From c3ff3dae8221eb436e0fb7911c83626b6aaf1825 Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:01:45 -0400 Subject: [PATCH 03/86] Resolve 5336 Mdm Link History Would Fail If Provided with Unknown Ids On Postgres (#5337) * - Added checks in MdmLinkDaoJpaImpl to make sure no empty IN clauses are sent to database. - Implemented tests for the above change. * modified changelog --- ...provided-with-unknown-ids-on-postgres.yaml | 6 + .../fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java | 33 +++- .../fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java | 175 ++++++++++++++++++ 3 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml new file mode 100644 index 00000000000..17aefdceb19 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5336 +jira: SMILE-7406 +title: "Previously, on PostgreSQL, the $mdm-link-history operation would fail if all ids provided to a parameter are +unknown, and the error will persist for all subsequent requests. This is now fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java index 6e87d443f1b..2206f36f829 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.hibernate.envers.AuditReader; import org.hibernate.envers.RevisionType; @@ -371,21 +372,39 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { final AuditQueryCreator auditQueryCreator = myAuditReader.createQuery(); try { - final AuditCriterion goldenResourceIdCriterion = AuditEntity.property(GOLDEN_RESOURCE_PID_NAME) - .in(convertToLongIds(theMdmHistorySearchParameters.getGoldenResourceIds())); - final AuditCriterion resourceIdCriterion = AuditEntity.property(SOURCE_PID_NAME) - .in(convertToLongIds(theMdmHistorySearchParameters.getSourceIds())); + final AuditCriterion goldenResourceIdCriterion = buildAuditCriterionOrNull( + theMdmHistorySearchParameters.getGoldenResourceIds(), GOLDEN_RESOURCE_PID_NAME); + + final AuditCriterion resourceIdCriterion = + buildAuditCriterionOrNull(theMdmHistorySearchParameters.getSourceIds(), SOURCE_PID_NAME); final AuditCriterion goldenResourceAndOrResourceIdCriterion; if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty() && !theMdmHistorySearchParameters.getSourceIds().isEmpty()) { + + // Make sure the criterion does not contain empty IN clause, e.g. id IN (), which postgres (likely other + // sql servers) do not like. Directly return empty result instead. + if (ObjectUtils.anyNull(goldenResourceIdCriterion, resourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = AuditEntity.and(goldenResourceIdCriterion, resourceIdCriterion); + } else if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()) { + + if (ObjectUtils.anyNull(goldenResourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = goldenResourceIdCriterion; + } else if (!theMdmHistorySearchParameters.getSourceIds().isEmpty()) { + + if (ObjectUtils.anyNull(resourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = resourceIdCriterion; + } else { throw new IllegalArgumentException(Msg.code(2298) + "$mdm-link-history Golden resource and source query IDs cannot both be empty."); @@ -420,6 +439,12 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { .collect(Collectors.toUnmodifiableList()); } + private AuditCriterion buildAuditCriterionOrNull( + List theMdmHistorySearchParameterIds, String theProperty) { + List longIds = convertToLongIds(theMdmHistorySearchParameterIds); + return longIds.isEmpty() ? null : AuditEntity.property(theProperty).in(longIds); + } + private MdmLinkWithRevision buildRevisionFromObjectArray(Object[] theArray) { final Object mdmLinkUncast = theArray[0]; final Object revisionUncast = theArray[1]; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java index 3078a06e0c0..39780e1a182 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java @@ -20,6 +20,8 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -276,6 +278,179 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test { assertEquals(2, actualMdmLinkRevisions.size(), "Both Patient/p123 and Practitioner/p123 should be returned"); } + @ParameterizedTest + @ValueSource(strings = {"allUnknown", "someUnknown"}) + public void testHistoryForUnknownIdsSourceIdOnly(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + List> expectedMdLinkRevisions = null; + switch (mode) { + // $mdm-link-history?resourceId=Patient/unknown + case "allUnknown" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters().setSourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown + case "someUnknown" -> { + List resourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + resourceIdsWithSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters().setSourceIds(resourceIdsWithSomeUnknown); + + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1) + ); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + + @ParameterizedTest + @ValueSource(strings = {"allUnknown", "someUnknown"}) + public void testHistoryForUnknownIdsGoldenResourceIdOnly(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + List> expectedMdLinkRevisions = null; + switch (mode) { + // $mdm-link-history?goldenResourceId=Patient/unknown + case "allUnknown" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters().setGoldenResourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?goldenResourceId=Patient/1,Patient/2,Patient/unknown + case "someUnknown" -> { + List resourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + resourceIdsWithSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters().setGoldenResourceIds(resourceIdsWithSomeUnknown); + + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId1_2 = mdmLinksWithLinkedPatients1.get(1).getSourcePersistenceId(); + final JpaPid sourceId1_3 = mdmLinksWithLinkedPatients1.get(2).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_2 = mdmLinksWithLinkedPatients2.get(1).getSourcePersistenceId(); + final JpaPid sourceId2_3 = mdmLinksWithLinkedPatients2.get(2).getSourcePersistenceId(); + final JpaPid sourceId2_4 = mdmLinksWithLinkedPatients2.get(3).getSourcePersistenceId(); + expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(2, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_2), + buildMdmLinkWithRevision(3, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_3), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1), + buildMdmLinkWithRevision(5, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_2), + buildMdmLinkWithRevision(6, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_3), + buildMdmLinkWithRevision(7, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_4) + ); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + + @ParameterizedTest + @ValueSource(strings = { + "allUnknownSourceId", + "allUnknownGoldenId", + "allUnknownBoth", + "someUnknownSourceId", + "someUnknownGoldenId", + "someUnknownBoth" + }) + public void testHistoryForUnknownIdsBothSourceAndGoldenResourceId(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + List> expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1) + ); + switch (mode) { + // $mdm-link-history?resourceId=Patient/unknown&goldenResourceId=Patient/1,Patient/2 + case "allUnknownSourceId" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(List.of("unknown")) + .setGoldenResourceIds(new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0)))); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2&goldenResourceId=Patient/unknown + case "allUnknownGoldenId" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0)))) + .setGoldenResourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/unknown&goldenResourceId=Patient/unknownGolden + case "allUnknownBoth" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(List.of("unknown")) + .setGoldenResourceIds(List.of("unknownGolden")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown&goldenResourceId=Patient/3,Patient/4 + case "someUnknownSourceId" -> { + List sourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + sourceIdsWithSomeUnknown.add("unknown"); + List goldenResourceIds = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIdsWithSomeUnknown) + .setGoldenResourceIds(goldenResourceIds); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2&goldenResourceId=Patient/3,Patient/4,Patient/unknown + case "someUnknownGoldenId" -> { + List sourceIds = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + List goldenResourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + goldenResourceIdsSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIds) + .setGoldenResourceIds(goldenResourceIdsSomeUnknown); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown&goldenResourceId=Patient/3,Patient/4,Patient/unknownGolden + case "someUnknownBoth" -> { + List sourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + sourceIdsSomeUnknown.add("unknown"); + List goldenResourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + goldenResourceIdsSomeUnknown.add("unknownGolden"); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIdsSomeUnknown) + .setGoldenResourceIds(goldenResourceIdsSomeUnknown); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + @Nonnull private static List getIdsFromMdmLinks(Function getIdFunction, MdmLink... mdmLinks) { return Arrays.stream(mdmLinks) From 7e0fa9833c05a244c51c8002ca6fadc0ffb75695 Mon Sep 17 00:00:00 2001 From: TipzCM Date: Fri, 29 Sep 2023 09:23:11 -0400 Subject: [PATCH 04/86] adding metrics (#5301) * hapi metrics * step 1 * metrics service * adding metrics service * adding documentation * refactor * refactoring * cleanup * changing * adding changelog * some refactoring * format * review fixes part 1 * review points * review fixes * updates for code review * updates for code review * bumping versions --------- Co-authored-by: leif stawnyczy Co-authored-by: leif stawnyczy --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- .../7_0_0/5274-adding-metric-svc-to-mdm.yaml | 6 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../data/IMdmLinkJpaMetricsRepository.java | 27 ++ .../fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java | 136 +++++++ .../java/ca/uhn/fhir/jpa/entity/MdmLink.java | 5 +- .../tasks/HapiFhirJpaMigrationTasks.java | 17 + .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- .../jpa/mdm/config/MdmConsumerConfig.java | 7 +- .../mdm/svc/GoldenResourceMergerSvcImpl.java | 3 +- .../fhir/jpa/mdm/svc/MdmEidUpdateService.java | 3 +- .../uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java | 3 +- .../jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java | 3 +- .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 6 + ...DaoSvc.java => MdmResourceDaoSvcImpl.java} | 32 +- .../svc/candidate/FindCandidateByEidSvc.java | 4 +- .../MdmGoldenResourceFindingSvc.java | 4 +- .../ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java | 2 - .../fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java | 172 +++++++++ .../jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java | 2 +- .../jpa/mdm/svc/MdmResourceDaoSvcTest.java | 5 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- .../uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java | 177 +++++++++ .../models/GenerateMetricsTestParameters.java | 31 ++ .../mdm/models/LinkMetricTestParameters.java | 68 ++++ .../mdm/models/LinkScoreMetricTestParams.java | 60 ++++ .../mdm/models/ResourceMetricTestParams.java | 61 ++++ .../ca/uhn/fhir/jpa/mdm/package-info.java | 6 + .../jpa/mdm/util/MdmMetricSvcTestUtil.java | 338 ++++++++++++++++++ hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- .../ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java | 94 +++++ .../ca/uhn/fhir/mdm/api/IMdmMetricSvc.java | 19 + .../uhn/fhir/mdm/api/IMdmResourceDaoSvc.java | 27 ++ .../ca/uhn/fhir/mdm/api/MdmConstants.java | 7 + .../GenerateMdmLinkMetricParameters.java | 60 ++++ .../params/GenerateMdmMetricsParameters.java | 69 ++++ .../GenerateMdmResourceMetricsParameters.java | 17 + .../GenerateScoreMetricsParameters.java | 39 ++ .../ca/uhn/fhir/mdm/model/MdmLinkMetrics.java | 52 +++ .../fhir/mdm/model/MdmLinkScoreMetrics.java | 35 ++ .../ca/uhn/fhir/mdm/model/MdmMetrics.java | 132 +++++++ .../fhir/mdm/model/MdmResourceMetrics.java | 60 ++++ .../fhir/mdm/model/MdmTransactionContext.java | 14 + .../fhir/mdm/util/GoldenResourceHelper.java | 8 + .../ca/uhn/fhir/mdm/util/MdmResourceUtil.java | 18 + .../mdm/util/MdmSearchParamBuildingUtils.java | 51 +++ .../fhir/mdm/util/MdmResourceUtilTest.java | 34 ++ hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- .../hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java | 4 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 120 files changed, 1955 insertions(+), 121 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java rename hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/{MdmResourceDaoSvc.java => MdmResourceDaoSvcImpl.java} (83%) create mode 100644 hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index d6f65b91794..17dbf26162e 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 3d3a0683523..150ce2c1295 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 8f8e138ac7a..98e10ec3c10 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index fb52d568b9f..346a929a547 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 7b4c1afe91d..4f4df91ba18 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-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 cbd07617e0f..c378ea02796 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 982c6c46480..04a840db292 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index cd84edbca81..a98004d7d99 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 08d53f813c0..b91cee3d080 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 38cac3e4603..ab4e11fa4c1 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index c067f7e19c9..f736e14d80d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index bef8a3b2810..a08d329cbee 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index e6042b475df..2d137c54951 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml new file mode 100644 index 00000000000..6d9b3be278f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5274 +title: "Added a service for generating metrics on mdm links and resources. + This includes JPA queries and updated indices. + " diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 5e82fe2a42b..89cc744fcc2 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 9ed9b7cc147..17ae3e225c7 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index cd0d62fd93a..32ffc32391c 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 223eb3c1ffa..954eac61b5f 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java new file mode 100644 index 00000000000..03566ca238a --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java @@ -0,0 +1,27 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository("metricsRepository") +public interface IMdmLinkJpaMetricsRepository extends JpaRepository, IHapiFhirJpaRepository { + + @Query("SELECT ml.myMatchResult AS match_result, ml.myLinkSource AS link_source, count(*) AS c " + + "FROM MdmLink ml " + + "WHERE ml.myMdmSourceType = :resourceName " + + "AND ml.myLinkSource in (:linkSource) " + + "AND ml.myMatchResult in (:matchResult) " + + "GROUP BY match_result, link_source " + + "ORDER BY match_result") + Object[][] generateMetrics( + @Param("resourceName") String theResourceType, + @Param("linkSource") List theLinkSources, + @Param("matchResult") List theMatchTypes); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java new file mode 100644 index 00000000000..6f1b4a57ff4 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java @@ -0,0 +1,136 @@ +package ca.uhn.fhir.jpa.dao.mdm; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaMetricsRepository; +import ca.uhn.fhir.mdm.api.BaseMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmLinkMetrics; +import ca.uhn.fhir.mdm.model.MdmLinkScoreMetrics; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Query; + +public class MdmMetricSvcJpaImpl extends BaseMdmMetricSvc { + + private final IMdmLinkJpaMetricsRepository myJpaRepository; + + private final EntityManagerFactory myEntityManagerFactory; + + public MdmMetricSvcJpaImpl( + IMdmLinkJpaMetricsRepository theRepository, + DaoRegistry theDaoRegistry, + EntityManagerFactory theEntityManagerFactory) { + super(theDaoRegistry); + myJpaRepository = theRepository; + myEntityManagerFactory = theEntityManagerFactory; + } + + protected MdmLinkMetrics generateLinkMetrics(GenerateMdmMetricsParameters theParameters) { + List linkSources = theParameters.getLinkSourceFilters(); + List matchResults = theParameters.getMatchResultFilters(); + + if (linkSources.isEmpty()) { + linkSources = Arrays.asList(MdmLinkSourceEnum.values()); + } + if (matchResults.isEmpty()) { + matchResults = Arrays.asList(MdmMatchResultEnum.values()); + } + + Object[][] data = myJpaRepository.generateMetrics(theParameters.getResourceType(), linkSources, matchResults); + MdmLinkMetrics metrics = new MdmLinkMetrics(); + metrics.setResourceType(theParameters.getResourceType()); + for (Object[] row : data) { + MdmMatchResultEnum matchResult = (MdmMatchResultEnum) row[0]; + MdmLinkSourceEnum source = (MdmLinkSourceEnum) row[1]; + long count = (Long) row[2]; + metrics.addMetric(matchResult, source, count); + } + return metrics; + } + + protected MdmLinkScoreMetrics generateLinkScoreMetrics(GenerateMdmMetricsParameters theParameters) { + String resourceType = theParameters.getResourceType(); + + List matchResultTypes = theParameters.getMatchResultFilters(); + + // if no result type filter, add all result types + if (matchResultTypes.isEmpty()) { + matchResultTypes = Arrays.asList(MdmMatchResultEnum.values()); + } + + String sql = "SELECT %s FROM MPI_LINK ml WHERE ml.TARGET_TYPE = :resourceType " + + "AND ml.MATCH_RESULT in (:matchResult)"; + + StringBuilder sb = new StringBuilder(); + sb.append("sum(case when ml.SCORE is null then 1 else 0 end) as B_" + NULL_VALUE); + + for (int i = 0; i < BUCKETS; i++) { + double bucket = getBucket(i + 1); + sb.append(",\n"); + if (i == 0) { + // score <= .01 + sb.append(String.format("sum(case when ml.SCORE <= %.2f then 1 else 0 end) as B%d", bucket, i)); + } else { + // score > i/100 && score <= i/100 + sb.append(String.format( + "sum(case when ml.score > %.2f and ml.SCORE <= %.2f then 1 else 0 end) as B%d", + getBucket(i), bucket, i)); + } + } + + EntityManager em = myEntityManagerFactory.createEntityManager(); + + Query nativeQuery = em.createNativeQuery(String.format(sql, sb.toString())); + + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) nativeQuery; + + hibernateQuery.setParameter("resourceType", resourceType); + hibernateQuery.setParameter( + "matchResult", matchResultTypes.stream().map(Enum::ordinal).collect(Collectors.toList())); + + List results = hibernateQuery.getResultList(); + + em.close(); + + MdmLinkScoreMetrics metrics = new MdmLinkScoreMetrics(); + + // we only get one row back + Object[] row = (Object[]) results.get(0); + int length = row.length; + for (int i = 0; i < length; i++) { + // if there's nothing in the db, these values will all be null + BigInteger bi = row[i] != null ? (BigInteger) row[i] : BigInteger.valueOf(0); + double bucket = getBucket(i); + if (i == 0) { + metrics.addScore(NULL_VALUE, bi.longValue()); + } else if (i == 1) { + metrics.addScore(String.format(FIRST_BUCKET, bucket), bi.longValue()); + } else { + metrics.addScore(String.format(NTH_BUCKET, getBucket(i - 1), bucket), bi.longValue()); + } + } + + return metrics; + } + + @Transactional + @Override + public MdmMetrics generateMdmMetrics(GenerateMdmMetricsParameters theParameters) { + MdmResourceMetrics resourceMetrics = generateResourceMetrics(theParameters); + MdmLinkMetrics linkMetrics = generateLinkMetrics(theParameters); + MdmLinkScoreMetrics scoreMetrics = generateLinkScoreMetrics(theParameters); + + MdmMetrics metrics = MdmMetrics.fromSeperableMetrics(resourceMetrics, linkMetrics, scoreMetrics); + return metrics; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java index 24e5063ab0c..4363bde74c5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java @@ -64,7 +64,10 @@ import javax.persistence.UniqueConstraint; @Index(name = "IDX_EMPI_MATCH_TGT_VER", columnList = "MATCH_RESULT, TARGET_PID, VERSION"), // v---- this one @Index(name = "IDX_EMPI_GR_TGT", columnList = "GOLDEN_RESOURCE_PID, TARGET_PID"), - @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID") + @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID"), + // indexes for metrics + @Index(name = "IDX_EMPI_TGT_MR_LS", columnList = "TARGET_TYPE, MATCH_RESULT, LINK_SOURCE"), + @Index(name = "IDX_EMPI_TGT_MR_SCORE", columnList = "TARGET_TYPE, MATCH_RESULT, SCORE") }) @Audited // This is the table name generated by default by envers, but we set it explicitly for clarity diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index b68af9bc768..c17de307498 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -93,6 +93,23 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { init640_after_20230126(); init660(); init680(); + init700(); + } + + protected void init700() { + Builder version = forVersion(VersionEnum.V7_0_0); + + // new indices on MdmLink + Builder.BuilderWithTableName mdmLinkTable = version.onTable("MPI_LINK"); + + mdmLinkTable + .addIndex("20230911.1", "IDX_EMPI_TGT_MR_LS") + .unique(false) + .withColumns("TARGET_TYPE", "MATCH_RESULT", "LINK_SOURCE"); + mdmLinkTable + .addIndex("20230911.2", "IDX_EMPi_TGT_MR_SCore") + .unique(false) + .withColumns("TARGET_TYPE", "MATCH_RESULT", "SCORE"); } protected void init680() { diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 6f7859823db..bee277633ec 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 452671378ab..a2ac4d063a4 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index c48e50f0fc4..db9e2677280 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 3ee942fbcd1..b7e925ea214 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 1d82d9e49a1..2c80e680249 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -39,7 +39,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchFinderSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmModelConverterSvcImpl; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; +import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateSearcher; import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByEidSvc; @@ -57,6 +57,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.batch2.MdmBatch2Config; import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider; @@ -123,8 +124,8 @@ public class MdmConsumerConfig { } @Bean - MdmResourceDaoSvc mdmResourceDaoSvc() { - return new MdmResourceDaoSvc(); + IMdmResourceDaoSvc mdmResourceDaoSvc() { + return new MdmResourceDaoSvcImpl(); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java index b1e15c91a40..fb60e425f57 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; @@ -74,7 +75,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { IIdHelperService myIdHelperService; @Autowired - MdmResourceDaoSvc myMdmResourceDaoSvc; + IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired MdmPartitionHelper myMdmPartitionHelper; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index 95e445012d3..52677a69b51 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -49,7 +50,7 @@ public class MdmEidUpdateService { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private IMdmLinkSvc myMdmLinkSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java index 3870b5d3393..027f69e3b7f 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -51,7 +52,7 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private MdmLinkDaoSvc myMdmLinkDaoSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java index 96892d37965..c37ff21be1d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -72,7 +73,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { MdmLinkDaoSvc myMdmLinkDaoSvc; @Autowired - MdmResourceDaoSvc myMdmResourceDaoSvc; + IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired MdmMatchLinkSvc myMdmMatchLinkSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index b7fba3ab877..f3512ea535a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.mdm.svc; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateStrategyEnum; @@ -70,6 +71,9 @@ public class MdmMatchLinkSvc { @Autowired private IBlockRuleEvaluationSvc myBlockRuleEvaluationSvc; + @Autowired + private DaoRegistry myDaoRegistry; + @Autowired private IMdmSurvivorshipService myMdmSurvivorshipService; @@ -106,6 +110,8 @@ public class MdmMatchLinkSvc { * (so that future resources may match to it). */ boolean isResourceBlocked = myBlockRuleEvaluationSvc.isMdmMatchingBlocked(theResource); + // we will mark the golden resource special for this + theMdmTransactionContext.setIsBlocked(isResourceBlocked); if (!isResourceBlocked) { FindGoldenResourceCandidatesParams params = diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java similarity index 83% rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java rename to hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java index 90d581ca649..2cf9489ee13 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java @@ -26,14 +26,15 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; +import ca.uhn.fhir.mdm.util.MdmSearchParamBuildingUtils; 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.api.server.storage.IResourcePersistentId; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -42,10 +43,9 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; @Service -public class MdmResourceDaoSvc { +public class MdmResourceDaoSvcImpl implements IMdmResourceDaoSvc { private static final int MAX_MATCHING_GOLDEN_RESOURCES = 1000; @@ -55,6 +55,7 @@ public class MdmResourceDaoSvc { @Autowired IMdmSettings myMdmSettings; + @Override public DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) @@ -66,12 +67,7 @@ public class MdmResourceDaoSvc { } } - /** - * Given a resource, remove its Golden Resource tag. - * - * @param theGoldenResource the {@link IAnyResource} to remove the tag from. - * @param theResourcetype the type of that resource - */ + @Override public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype); RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) @@ -84,18 +80,22 @@ public class MdmResourceDaoSvc { requestDetails); } + @Override public IAnyResource readGoldenResourceByPid(IResourcePersistentId theGoldenResourcePid, String theResourceType) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); return (IAnyResource) resourceDao.readByPid(theGoldenResourcePid); } + @Override public Optional searchGoldenResourceByEID(String theEid, String theResourceType) { return this.searchGoldenResourceByEID(theEid, theResourceType, null); } + @Override public Optional searchGoldenResourceByEID( String theEid, String theResourceType, RequestPartitionId thePartitionId) { - SearchParameterMap map = buildEidSearchParameterMap(theEid, theResourceType); + SearchParameterMap map = MdmSearchParamBuildingUtils.buildEidSearchParameterMap( + theEid, theResourceType, myMdmSettings.getMdmRules()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); @@ -118,16 +118,4 @@ public class MdmResourceDaoSvc { return Optional.of((IAnyResource) resources.get(0)); } } - - @Nonnull - private SearchParameterMap buildEidSearchParameterMap(String theEid, String theResourceType) { - SearchParameterMap map = new SearchParameterMap(); - map.setLoadSynchronous(true); - map.add( - "identifier", - new TokenParam( - myMdmSettings.getMdmRules().getEnterpriseEIDSystemForResourceType(theResourceType), theEid)); - map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); - return map; - } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java index 2ab9e19ace0..e887cb47b39 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java @@ -21,8 +21,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLink; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.CanonicalEID; @@ -47,7 +47,7 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder { private EIDHelper myEIDHelper; @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private MdmLinkDaoSvc myMdmLinkDaoSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java index 073ca1ced2b..c6f235eda67 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; @@ -36,7 +36,7 @@ public class MdmGoldenResourceFindingSvc { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private FindCandidateByEidSvc myFindCandidateByEidSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index e1474eada1a..3e9d31edb6b 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -98,8 +98,6 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { .setValue("555-555-5555"); private static final String NAME_GIVEN_FRANK = "Frank"; - - @Autowired protected IFhirResourceDao myPatientDao; @Autowired diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java new file mode 100644 index 00000000000..77a186cc549 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java @@ -0,0 +1,172 @@ +package ca.uhn.fhir.jpa.mdm.dao; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaMetricsRepository; +import ca.uhn.fhir.jpa.dao.mdm.MdmMetricSvcJpaImpl; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest; +import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; +import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState; +import ca.uhn.fhir.jpa.mdm.models.GenerateMetricsTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.mdm.api.IMdmMetricSvc; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.util.MdmResourceUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import javax.persistence.EntityManagerFactory; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.fail; + +@ContextConfiguration(classes = { + MdmMetricSvcJpaIT.TestConfig.class +}) +public class MdmMetricSvcJpaIT extends BaseMdmR4Test implements IMdmMetricSvcTest { + + private static final Logger ourLog = LoggerFactory.getLogger(MdmMetricSvcJpaIT.class); + + @Configuration + public static class TestConfig { + + @Autowired + @Qualifier("metricsRepository") + private IMdmLinkJpaMetricsRepository myJpaRepository; + + @Autowired + private DaoRegistry myDaoRegistry; + + @Autowired + private EntityManagerFactory myEntityManagerFactory; + + @Autowired + private HapiFhirLocalContainerEntityManagerFactoryBean myEntityFactory; + + // this has to be provided via spring, or the + // @Transactional barrier is never invoked + @Bean + IMdmMetricSvc mdmMetricSvc() { + return new MdmMetricSvcJpaImpl( + myJpaRepository, + myDaoRegistry, + myEntityManagerFactory + ); + } + } + + private final ObjectMapper myObjectMapper = new ObjectMapper(); + + @Autowired + private MdmLinkHelper myLinkHelper; + + @Autowired + private IMdmMetricSvc mySvc; + + @BeforeEach + public void before() throws Exception { + super.before(); + } + + @Override + public IMdmMetricSvc getMetricsSvc() { + return mySvc; + } + + @Override + public void generateMdmMetricsSetup(GenerateMetricsTestParameters theParameters) { + if (StringUtils.isNotBlank(theParameters.getInitialState())) { + MDMState state = new MDMState<>(); + state.setInputState(theParameters.getInitialState()); + myLinkHelper.setup(state); + + // update scores if needed + setupScores(theParameters.getScores()); + } + } + + @Override + public void generateLinkMetricsSetup(LinkMetricTestParameters theParameters) { + ourLog.info(theParameters.getInitialState()); + if (StringUtils.isNotBlank(theParameters.getInitialState())) { + // we can only initialize the state if there is a state to initialize + MDMState state = new MDMState<>(); + state.setInputState(theParameters.getInitialState()); + myLinkHelper.setup(state); + } + } + + @Override + public void generateResourceMetricsSetup(ResourceMetricTestParams theParams) { + MDMState state = new MDMState<>(); + String initialState = theParams.getInitialState(); + if (StringUtils.isNotBlank(initialState)) { + state.setInputState(initialState); + + for (String forcedBlockedGRId : theParams.getBlockedResourceGoldenResourceIds()) { + Patient gr = new Patient(); + gr.setActive(true); + gr.setId("Patient/" + forcedBlockedGRId); + MdmResourceUtil.setMdmManaged(gr); + MdmResourceUtil.setGoldenResource(gr); + MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource(gr); + + Patient p = createPatient(gr, true, false); + state.addParameter(forcedBlockedGRId, p); + } + + myLinkHelper.setup(state); + } + } + + @Override + public void generateLinkScoreMetricsSetup(LinkScoreMetricTestParams theParams) { + MDMState state = new MDMState<>(); + String initialState = theParams.getInitialState(); + + if (StringUtils.isNotBlank(initialState)) { + state.setInputState(initialState); + + myLinkHelper.setup(state); + + // update scores if needed + setupScores(theParams.getScores()); + } + } + + private void setupScores(List theParams) { + List links = myMdmLinkDao.findAll(); + for (int i = 0; i < theParams.size() && i < links.size(); i++) { + Double score = theParams.get(i); + MdmLink link = links.get(i); + link.setScore(score); + myMdmLinkDao.save(link); + } + } + + @Override + public String getStringMetrics(MdmMetrics theMetrics) { + try { + return myObjectMapper.writeValueAsString(theMetrics); + } catch (JsonProcessingException ex) { + // we've failed anyway - we might as well display the exception + fail(ex); + return "NOT PARSEABLE!"; + } + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java index 6433348881e..549f818f399 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java @@ -41,7 +41,7 @@ class MdmLinkUpdaterSvcImplIT extends BaseMdmR4Test { private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc; @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private MdmResourceDaoSvcImpl myMdmResourceDaoSvc; @Autowired private MessageHelper myMessageHelper; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java index 1bd33081e2f..e51949689c4 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java @@ -7,13 +7,13 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterEach; @@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -35,7 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class MdmResourceDaoSvcTest extends BaseMdmR4Test { private static final String TEST_EID = "TEST_EID"; @Autowired - MdmResourceDaoSvc myResourceDaoSvc; + IMdmResourceDaoSvc myResourceDaoSvc; @Autowired private ISearchParamExtractor mySearchParamExtractor; diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 27c8d0a0379..0eb9cabc642 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 3fe94ad2c5f..a2728494bf4 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 972d0aca525..ed3cab96c80 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 - 6.9.8-SNAPSHOT + 6.9.9-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 6fe814dfd00..2e00cf23d76 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index de5da25656a..f03dae46f3b 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 74e8f394246..9253e66d122 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index ffe2b31be48..22ad4ef0907 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 2e85a2a2bd4..1a9b4c1527a 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 0c810ee1268..c5a65a61dd0 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java new file mode 100644 index 00000000000..18b51d0bc28 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java @@ -0,0 +1,177 @@ +package ca.uhn.fhir.jpa.mdm; + +import ca.uhn.fhir.jpa.mdm.models.GenerateMetricsTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil; +import ca.uhn.fhir.mdm.api.IMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests the various metrics returned by IMdmMetricSvc + * Because of the way these metrics are broken down in 3 different ways, + * these results are tested separately, even though there is a single + * entry point. + */ +public interface IMdmMetricSvcTest { + + IMdmMetricSvc getMetricsSvc(); + + void generateMdmMetricsSetup(GenerateMetricsTestParameters theParameters); + + @Test + default void generateMdmMetrics_generalTest_happyPath() { + // setup + GenerateMetricsTestParameters testParameters = new GenerateMetricsTestParameters(); + testParameters.setInitialState(MdmMetricSvcTestUtil.OUR_BASIC_STATE); + testParameters.setScores(Arrays.asList(0.1, 0.2, 0.3, 0.4)); + + generateMdmMetricsSetup(testParameters); + + // test + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + MdmMetrics results = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(results); + assertEquals("Patient", results.getResourceType()); + assertEquals(4, results.getGoldenResourcesCount()); + assertEquals(4, results.getSourceResourcesCount()); + assertEquals(0, results.getExcludedResources()); + + Map> map = results.getMatchTypeToLinkToCountMap(); + // See OUR_BASIC_STATE + assertEquals(3, map.size()); + for (MdmMatchResultEnum matchResult : new MdmMatchResultEnum[] { + MdmMatchResultEnum.MATCH, MdmMatchResultEnum.NO_MATCH, MdmMatchResultEnum.POSSIBLE_MATCH + }) { + assertTrue(map.containsKey(matchResult)); + Map source2Count = map.get(matchResult); + assertNotNull(source2Count); + for (MdmLinkSourceEnum ls : MdmLinkSourceEnum.values()) { + assertNotNull(source2Count.get(ls)); + } + } + } + + void generateLinkMetricsSetup(LinkMetricTestParameters theParameters); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#linkMetricsParameters") + default void test_generateLinkMetrics_multipleInputs(LinkMetricTestParameters theParameters) { + // setup + generateLinkMetricsSetup(theParameters); + + // all tests use Patient resource type + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + for (MdmLinkSourceEnum linkSource : theParameters.getLinkSourceFilters()) { + parameters.addLinkSource(linkSource); + } + for (MdmMatchResultEnum matchResultEnum : theParameters.getMatchFilters()) { + parameters.addMatchResult(matchResultEnum); + } + + // test + MdmMetrics metrics = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(metrics); + assertEquals(metrics.getResourceType(), "Patient"); + + MdmMetrics expectedMetrics = theParameters.getExpectedMetrics(); + + Supplier err = () -> getComparingMetrics(metrics, expectedMetrics); + + Map> actual = metrics.getMatchTypeToLinkToCountMap(); + Map> expected = expectedMetrics.getMatchTypeToLinkToCountMap(); + assertEquals(expected, actual, err.get()); + + for (MdmMatchResultEnum matchResult : MdmMatchResultEnum.values()) { + assertEquals(expected.containsKey(matchResult), actual.containsKey(matchResult), err.get()); + if (actual.containsKey(matchResult)) { + Map actualMatch = actual.get(matchResult); + Map expectedMatch = expected.get(matchResult); + assertEquals(expectedMatch, actualMatch, err.get()); + for (MdmLinkSourceEnum linkSource : MdmLinkSourceEnum.values()) { + assertEquals(expectedMatch.get(linkSource), actualMatch.get(linkSource), err.get()); + } + } + } + } + + void generateResourceMetricsSetup(ResourceMetricTestParams theParams); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#resourceMetricParameters") + default void test_generateResourceMetrics_multipleInputs(ResourceMetricTestParams theParams) { + // setup + generateResourceMetricsSetup(theParams); + + // test + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + MdmResourceMetrics results = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(results); + assertEquals("Patient", results.getResourceType()); + assertEquals( + theParams.getExpectedResourceCount(), + results.getSourceResourcesCount() + results.getGoldenResourcesCount()); + assertEquals(theParams.getExpectedBlockedResourceCount(), results.getExcludedResources()); + assertEquals(theParams.getExpectedGoldenResourceCount(), results.getGoldenResourcesCount()); + } + + void generateLinkScoreMetricsSetup(LinkScoreMetricTestParams theParams); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#linkScoreParameters") + default void test_generateLinkScoreMetrics_multipleInputs(LinkScoreMetricTestParams theParams) { + // setup + generateLinkScoreMetricsSetup(theParams); + + GenerateMdmMetricsParameters scoreMetricsParameters = new GenerateMdmMetricsParameters("Patient"); + for (MdmMatchResultEnum matchType : theParams.getMatchFilter()) { + scoreMetricsParameters.addMatchResult(matchType); + } + + // test + MdmMetrics actualMetrics = getMetricsSvc().generateMdmMetrics(scoreMetricsParameters); + + // verify + assertNotNull(actualMetrics); + assertEquals("Patient", actualMetrics.getResourceType()); + + MdmMetrics expectedMetrics = theParams.getExpectedMetrics(); + + Map actual = actualMetrics.getScoreCounts(); + Map expected = expectedMetrics.getScoreCounts(); + assertEquals(expected.size(), actual.size()); + for (String score : expected.keySet()) { + assertTrue(actual.containsKey(score), String.format("Score of %s is not in results", score)); + assertEquals(expected.get(score), actual.get(score), score); + } + } + + private String getComparingMetrics(MdmMetrics theActual, MdmMetrics theExpected) { + return String.format( + "\nExpected: \n%s - \nActual: \n%s", getStringMetrics(theExpected), getStringMetrics(theActual)); + } + + String getStringMetrics(MdmMetrics theMetrics); +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java new file mode 100644 index 00000000000..8afeb438902 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.jpa.mdm.models; + +import java.util.List; + +public class GenerateMetricsTestParameters { + + private String myInitialState; + + /** + * The scores for each link. + * The order should match the order of the + * links listed in initial state. + */ + private List myScores; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getScores() { + return myScores; + } + + public void setScores(List theScores) { + myScores = theScores; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java new file mode 100644 index 00000000000..95ba075b645 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java @@ -0,0 +1,68 @@ +package ca.uhn.fhir.jpa.mdm.models; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.List; + +public class LinkMetricTestParameters { + /** + * The initial state (as to be fed into MdmLinkHelper) + */ + private String myInitialState; + + /** + * The filters for MatchResult + */ + private List myMatchFilters; + + /** + * The filters for LinkSource + */ + private List myLinkSourceEnums; + + /** + * The expected metrics to be returned + */ + private MdmMetrics myExpectedMetrics; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getMatchFilters() { + if (myMatchFilters == null) { + myMatchFilters = new ArrayList<>(); + } + return myMatchFilters; + } + + public void setMatchFilters(List theMatchFilters) { + myMatchFilters = theMatchFilters; + } + + public List getLinkSourceFilters() { + if (myLinkSourceEnums == null) { + myLinkSourceEnums = new ArrayList<>(); + } + return myLinkSourceEnums; + } + + public void setLinkSourceFilters(List theLinkSourceEnums) { + myLinkSourceEnums = theLinkSourceEnums; + } + + public MdmMetrics getExpectedMetrics() { + return myExpectedMetrics; + } + + public void setExpectedMetrics(MdmMetrics theExpectedMetrics) { + myExpectedMetrics = theExpectedMetrics; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java new file mode 100644 index 00000000000..0434ccb69fb --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java @@ -0,0 +1,60 @@ +package ca.uhn.fhir.jpa.mdm.models; + +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.List; + +public class LinkScoreMetricTestParams { + private String myInitialState; + + private List myMatchFilter; + + private MdmMetrics myExpectedMetrics; + + /** + * The scores for each link. + * The order should match the order of the + * links listed in initial state. + */ + private List myScores; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public MdmMetrics getExpectedMetrics() { + return myExpectedMetrics; + } + + public void setExpectedMetrics(MdmMetrics theExpectedMetrics) { + myExpectedMetrics = theExpectedMetrics; + } + + public List getMatchFilter() { + if (myMatchFilter == null) { + myMatchFilter = new ArrayList<>(); + } + return myMatchFilter; + } + + public void addMatchType(MdmMatchResultEnum theResultEnum) { + getMatchFilter().add(theResultEnum); + } + + public List getScores() { + if (myScores == null) { + myScores = new ArrayList<>(); + } + return myScores; + } + + public void setScores(List theScores) { + myScores = theScores; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java new file mode 100644 index 00000000000..4ed1b52972c --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java @@ -0,0 +1,61 @@ +package ca.uhn.fhir.jpa.mdm.models; + +import java.util.ArrayList; +import java.util.List; + +public class ResourceMetricTestParams { + /** + * The initial state, as consumable by + * MdmLinkHelper. + */ + private String myInitialState; + + /** + * The list of Golden Resource Ids (in initial state) that should be + * saved as BlockedResources + */ + private List myBlockedResourceGoldenResourceIds; + + private long myExpectedResourceCount; + + private long myExpectedGoldenResourceCount; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getBlockedResourceGoldenResourceIds() { + if (myBlockedResourceGoldenResourceIds == null) { + myBlockedResourceGoldenResourceIds = new ArrayList<>(); + } + return myBlockedResourceGoldenResourceIds; + } + + public void addBlockedResourceGoldenResources(String theBlockedResourceId) { + getBlockedResourceGoldenResourceIds().add(theBlockedResourceId); + } + + public long getExpectedResourceCount() { + return myExpectedResourceCount; + } + + public void setExpectedResourceCount(long theExpectedResourceCount) { + myExpectedResourceCount = theExpectedResourceCount; + } + + public long getExpectedGoldenResourceCount() { + return myExpectedGoldenResourceCount; + } + + public void setExpectedGoldenResourceCount(long theExpectedGoldenResourceCount) { + myExpectedGoldenResourceCount = theExpectedGoldenResourceCount; + } + + public long getExpectedBlockedResourceCount() { + return getBlockedResourceGoldenResourceIds().size(); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java new file mode 100644 index 00000000000..13da8304544 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java @@ -0,0 +1,6 @@ +/** + * This package is for persistence-agnostic mdm tests. + * Even though the package is "jpaserver-test-utils", these + * classes are not dependent on jpa backed persistence. + */ +package ca.uhn.fhir.jpa.mdm; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java new file mode 100644 index 00000000000..9e5a7503dcd --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java @@ -0,0 +1,338 @@ +package ca.uhn.fhir.jpa.mdm.util; + +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.mdm.api.BaseMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +/** + * This provides parameter methods for the {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest}. + */ +public class MdmMetricSvcTestUtil { + + public static final String OUR_BASIC_STATE = + """ + G1, AUTO, MATCH, P1 + G2, AUTO, MATCH, P2, + G3, AUTO, POSSIBLE_MATCH, P3, + G4, MANUAL, MATCH, P4 + G2, AUTO, NO_MATCH, P1 + G1, MANUAL, NO_MATCH, P2 + G1, MANUAL, POSSIBLE_MATCH, P3 + """; + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#test_generateLinkMetrics_multipleInputs(LinkMetricTestParameters)} + */ + public static List linkMetricsParameters() { + List params = new ArrayList<>(); + + // 1 + { + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 2 + { + // link source filter + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 3 + { + // match result filter + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH, MdmMatchResultEnum.POSSIBLE_MATCH)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 4 + { + // match result and link source filters + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.MANUAL)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 5 + { + // no initial state + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""); + MdmMetrics metrics = new MdmMetrics(); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 6 + { + // initial state with filters to omit all values + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""" + G1, AUTO, NO_MATCH, P1 + G2, MANUAL, MATCH, P2 + """); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + testParameters.setExpectedMetrics(new MdmMetrics()); + params.add(testParameters); + } + + // 7 + { + // initial state with filters to omit some values + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""" + G1, AUTO, NO_MATCH, P1 + G2, MANUAL, MATCH, P2 + """); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.NO_MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + return params; + } + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#test_generateResourceMetrics_multipleInputs(ResourceMetricTestParams)} + */ + public static List resourceMetricParameters() { + List params = new ArrayList<>(); + + // 1 + { + // a mix of golden, regular, and blocked resources + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, MATCH, P1 + G2, AUTO, MATCH, P2 + G2, AUTO, MATCH, P1, + G3, AUTO, MATCH, P3 + """); + p.addBlockedResourceGoldenResources("G2"); + p.addBlockedResourceGoldenResources("G3"); + p.setExpectedResourceCount(6); + p.setExpectedGoldenResourceCount(3); + params.add(p); + } + + // 2 + { + // 2 non-golden, 1 golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, MATCH, P1, + G1, MANUAL, MATCH, P2 + """); + p.setExpectedResourceCount(3); + p.setExpectedGoldenResourceCount(1); + params.add(p); + } + + // 3 + { + // 2 golden, 1 non-golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, MATCH, P1 + G2, AUTO, POSSIBLE_DUPLICATE, G1 + """); + p.setExpectedGoldenResourceCount(2); + p.setExpectedResourceCount(3); + params.add(p); + } + + // 4 + { + // 2 golden, 1 blocked, 0 non-golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, POSSIBLE_DUPLICATE, G2 + """); + p.addBlockedResourceGoldenResources("G1"); + p.setExpectedResourceCount(2); + p.setExpectedGoldenResourceCount(2); + params.add(p); + } + + // 5 + { + // no resources + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""); + params.add(p); + } + + return params; + } + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#generateLinkScoreMetricsSetup(LinkScoreMetricTestParams)} + */ + public static List linkScoreParameters() { + List parameters = new ArrayList<>(); + + // 1 + { + // score counts + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, MATCH, P1 + G2, AUTO, POSSIBLE_MATCH, P2, + G3, AUTO, POSSIBLE_MATCH, P1 + """); + p.setScores(Arrays.asList(.2D, .2D, .1D)); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 2 + { + // a null score + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState(""" + G1, AUTO, POSSIBLE_MATCH, P1, + G2, AUTO, POSSIBLE_MATCH, P2 + """); + p.setScores(Arrays.asList(null, 0.02D)); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 3 + { + // match type filtering + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, POSSIBLE_MATCH, P1 + G2, AUTO, MATCH, P2 + G3, AUTO, POSSIBLE_MATCH, P3 + G4, AUTO, MATCH, P4 + """); + p.setScores(Arrays.asList(0.4D, 0.4D, 0.1D, 0.3D)); + p.addMatchType(MdmMatchResultEnum.POSSIBLE_MATCH); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 4 + { + // no links + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState(""); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + p.setExpectedMetrics(metrics); + populateScoreIntoMetrics(p, metrics); + parameters.add(p); + } + + return parameters; + } + + private static void populateScoreIntoMetrics(LinkScoreMetricTestParams p, MdmMetrics metrics) { + String initialState = p.getInitialState(); + Map indexToMatchResult = new HashMap<>(); + if (isNotBlank(initialState)) { + String[] states = initialState.split("\n"); + int len = states.length; + for (int i = 0; i < len; i++) { + String state = states[i]; + String[] values = state.split(","); + indexToMatchResult.put(i, MdmMatchResultEnum.valueOf(values[2].trim())); + } + } + + Map score2Count = new HashMap<>(); + long nullCount = 0; + for (int i = 0; i < p.getScores().size(); i++) { + MdmMatchResultEnum matchResult = indexToMatchResult.get(i); + // if it's not a filtered value, add it to the expected metrics + if (p.getMatchFilter().isEmpty() || p.getMatchFilter().contains(matchResult)) { + Double d = p.getScores().get(i); + if (d == null) { + nullCount++; + } else { + if (!score2Count.containsKey(d)) { + score2Count.put(d, 0L); + } + score2Count.put(d, score2Count.get(d) + 1); + } + } + } + metrics.addScore(BaseMdmMetricSvc.NULL_VALUE, nullCount); + for (int i = 0; i < BaseMdmMetricSvc.BUCKETS; i++) { + double bucket = (double) Math.round((float) (100 * (i + 1)) / BaseMdmMetricSvc.BUCKETS) / 100; + long count = 0; + // TODO - do not add it if the corresponding link does not have + // the correct MATCH_RESULT value + if (score2Count.containsKey(bucket)) { + count = score2Count.get(bucket); + } + if (i == 0) { + metrics.addScore(String.format(BaseMdmMetricSvc.FIRST_BUCKET, bucket), count); + } else { + metrics.addScore( + String.format(BaseMdmMetricSvc.NTH_BUCKET, (float) i / BaseMdmMetricSvc.BUCKETS, bucket), + count); + } + } + } +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 883d501d672..dbb6efb3f9c 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index feac29e26be..266e0577cf8 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 03faeaa9b70..f01dc291872 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java new file mode 100644 index 00000000000..a3a9f75f56d --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java @@ -0,0 +1,94 @@ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import ca.uhn.fhir.mdm.util.MdmSearchParamBuildingUtils; +import ca.uhn.fhir.rest.api.SearchTotalModeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; + +public abstract class BaseMdmMetricSvc implements IMdmMetricSvc { + + /** + * Count of numbered buckets. + * There will also be a NULL bucket, so there will be a total + * of BUCKETS + 1 buckets. + */ + public static final int BUCKETS = 100; + + /** + * The NULL label + */ + public static final String NULL_VALUE = "NULL"; + + /** + * The label for the first bucket + */ + public static final String FIRST_BUCKET = "x_<_%.2f"; + + /** + * The label for the nth bucket (2... buckets) + */ + public static final String NTH_BUCKET = "%.2f_<_x_<=_%.2f"; + + protected final DaoRegistry myDaoRegistry; + + public BaseMdmMetricSvc(DaoRegistry theDaoRegistry) { + myDaoRegistry = theDaoRegistry; + } + + protected double getBucket(int theBucketId) { + return (double) Math.round((float) (100 * theBucketId) / BUCKETS) / 100; + } + + protected MdmResourceMetrics generateResourceMetrics(GenerateMdmMetricsParameters theParameters) { + String resourceType = theParameters.getResourceType(); + @SuppressWarnings("rawtypes") + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); + + // TODO + /* + * We are using 3 different queries to count: + * * all resources + * * all golden resources + * * all blocked resources. + * + * This is inefficient and if we want, we can speed it up with + * a custom query in the future. + */ + IBundleProvider outcome = null; + SearchParameterMap map = null; + + MdmResourceMetrics metrics = new MdmResourceMetrics(); + metrics.setResourceType(resourceType); + + // find golden resources + map = MdmSearchParamBuildingUtils.buildBasicGoldenResourceSearchParameterMap(resourceType); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setGoldenResourcesCount(outcome.size()); + + // find blocked resources + map = MdmSearchParamBuildingUtils.buildSearchParameterForBlockedResourceCount(resourceType); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setExcludedResources(outcome.size()); + + // find all resources + map = new SearchParameterMap(); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setSourceResourcesCount(outcome.size() - metrics.getGoldenResourcesCount()); + + return metrics; + } + + private void setCountOnly(SearchParameterMap theMap) { + theMap.setCount(0); + theMap.setLoadSynchronous(true); + theMap.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java new file mode 100644 index 00000000000..451239bd243 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java @@ -0,0 +1,19 @@ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +public interface IMdmMetricSvc { + + /** + * Generates metrics on MDM Links. + * Metrics include: + * * breakdowns of counts of MATCH_RESULT types by LINK_SOURCE types. + * * counts of resources of each type + * * a histogram of score 'buckets' with the appropriate counts. + * @param theParameters - Parameters defining resource type of interest, + * as well as MatchResult and LinkSource filters. + * @return The metrics in a JSON format. + */ + MdmMetrics generateMdmMetrics(GenerateMdmMetricsParameters theParameters); +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java new file mode 100644 index 00000000000..7afe2aa388e --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java @@ -0,0 +1,27 @@ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import org.hl7.fhir.instance.model.api.IAnyResource; + +import java.util.Optional; + +public interface IMdmResourceDaoSvc { + DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType); + + /** + * Given a resource, remove its Golden Resource tag. + * + * @param theGoldenResource the {@link IAnyResource} to remove the tag from. + * @param theResourcetype the type of that resource + */ + void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype); + + IAnyResource readGoldenResourceByPid(IResourcePersistentId theGoldenResourcePid, String theResourceType); + + Optional searchGoldenResourceByEID(String theEid, String theResourceType); + + Optional searchGoldenResourceByEID( + String theEid, String theResourceType, RequestPartitionId thePartitionId); +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java index 7d35442c202..0207577abd7 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java @@ -34,6 +34,13 @@ public class MdmConstants { "http://hapifhir.io/fhir/NamingSystem/mdm-golden-resource-enterprise-id"; public static final String ALL_RESOURCE_SEARCH_PARAM_TYPE = "*"; + /** + * Blocked resource tag info + */ + public static final String CODE_BLOCKED = "BLOCKED_RESOURCE"; + + public static final String CODE_BLOCKED_DISPLAY = "Source Resource is omitted from MDM matching."; + public static final String FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/match-grade"; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java new file mode 100644 index 00000000000..b8f0c2bfcbc --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java @@ -0,0 +1,60 @@ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateMdmLinkMetricParameters { + + /** + * The resource type of interest. + * Must be provided! + */ + private final String myResourceType; + + /** + * The MDM MatchResult types of interest. + * Specified MatchResults will be included. + * If none are specified, all will be included. + */ + private List myMatchResultFilters; + + /** + * The MDM Link values of interest. + * Specified LinkSources will be included. + * If none are specified, all are included. + */ + private List myLinkSourceFilters; + + public GenerateMdmLinkMetricParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchResultFilters() { + if (myMatchResultFilters == null) { + myMatchResultFilters = new ArrayList<>(); + } + return myMatchResultFilters; + } + + public void addMatchResultFilter(MdmMatchResultEnum theMdmMatchResultEnum) { + getMatchResultFilters().add(theMdmMatchResultEnum); + } + + public List getLinkSourceFilters() { + if (myLinkSourceFilters == null) { + myLinkSourceFilters = new ArrayList<>(); + } + return myLinkSourceFilters; + } + + public void addLinkSourceFilter(MdmLinkSourceEnum theLinkSource) { + getLinkSourceFilters().add(theLinkSource); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java new file mode 100644 index 00000000000..98dd96b8366 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java @@ -0,0 +1,69 @@ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateMdmMetricsParameters { + + /** + * We only allow finding metrics by resource type + */ + private final String myResourceType; + + /** + * The MDM MatchResult types of interest. + * Specified MatchResults will be included. + * If none are specified, all will be included. + */ + private List myMatchResultFilters; + + /** + * The MDM Link values of interest. + * Specified LinkSources will be included. + * If none are specified, all are included. + */ + private List myLinkSourceFilters; + + public GenerateMdmMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchResultFilters() { + if (myMatchResultFilters == null) { + myMatchResultFilters = new ArrayList<>(); + } + return myMatchResultFilters; + } + + public void addMatchResult(MdmMatchResultEnum theMdmMatchResultEnum) { + getMatchResultFilters().add(theMdmMatchResultEnum); + } + + public List getLinkSourceFilters() { + if (myLinkSourceFilters == null) { + myLinkSourceFilters = new ArrayList<>(); + } + return myLinkSourceFilters; + } + + public void addLinkSource(MdmLinkSourceEnum theLinkSource) { + getLinkSourceFilters().add(theLinkSource); + } + + // public GenerateMdmLinkMetricParameters toLinkMetricParams() { + // + // } + // + // public GenerateMdmResourceMetricsParameters toResourceMetricParams() { + // + // } + // + +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java new file mode 100644 index 00000000000..504f169604e --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.mdm.api.params; + +public class GenerateMdmResourceMetricsParameters { + + /** + * We only allow finding metrics by resource type + */ + private final String myResourceType; + + public GenerateMdmResourceMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java new file mode 100644 index 00000000000..412703585de --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java @@ -0,0 +1,39 @@ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateScoreMetricsParameters { + /** + * The resource type of interest. + */ + private final String myResourceType; + + /** + * MatchResult types to filter for. + * Specified MatchResults will be included. + * If none specified, all will be included. + */ + private List myMatchTypeFilters; + + public GenerateScoreMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchTypes() { + if (myMatchTypeFilters == null) { + myMatchTypeFilters = new ArrayList<>(); + } + return myMatchTypeFilters; + } + + public void addMatchType(MdmMatchResultEnum theMatchType) { + getMatchTypes().add(theMatchType); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java new file mode 100644 index 00000000000..9d30b033030 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java @@ -0,0 +1,52 @@ +package ca.uhn.fhir.mdm.model; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.HashMap; +import java.util.Map; + +public class MdmLinkMetrics { + /** + * The resource type to which these metrics apply. + */ + private String myResourceType; + + /** + * A mapping of MatchType -> LinkSource -> count. + * Eg: + * MATCH + * AUTO - 2 + * MANUAL - 1 + * NO_MATCH + * AUTO - 1 + * MANUAL - 3 + */ + private Map> myMatchTypeToLinkToCountMap; + + public String getResourceType() { + return myResourceType; + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public Map> getMatchTypeToLinkToCountMap() { + if (myMatchTypeToLinkToCountMap == null) { + myMatchTypeToLinkToCountMap = new HashMap<>(); + } + return myMatchTypeToLinkToCountMap; + } + + public void addMetric( + MdmMatchResultEnum theMdmMatchResultEnum, MdmLinkSourceEnum theLinkSourceEnum, long theCount) { + Map> map = getMatchTypeToLinkToCountMap(); + + if (!map.containsKey(theMdmMatchResultEnum)) { + map.put(theMdmMatchResultEnum, new HashMap<>()); + } + Map lsToCountMap = map.get(theMdmMatchResultEnum); + lsToCountMap.put(theLinkSourceEnum, theCount); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java new file mode 100644 index 00000000000..d5ea731ddea --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java @@ -0,0 +1,35 @@ +package ca.uhn.fhir.mdm.model; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class MdmLinkScoreMetrics { + + private String myResourceType; + + /** + * Map of Score:Count + * Scores are typically Doubles. But we cast to string because + * Score is not a non-null field, and so "NULL" is a value. + */ + private Map myScoreCounts; + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public Map getScoreCounts() { + if (myScoreCounts == null) { + myScoreCounts = new LinkedHashMap<>(); + } + return myScoreCounts; + } + + public void addScore(String theScore, Long theCount) { + getScoreCounts().put(theScore, theCount); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java new file mode 100644 index 00000000000..aed9fbe9f76 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java @@ -0,0 +1,132 @@ +package ca.uhn.fhir.mdm.model; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.model.api.IModelJson; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class MdmMetrics extends MdmResourceMetrics implements IModelJson { + + @JsonProperty("resourceType") + private String myResourceType; + + /** + * A mapping of MatchType -> LinkSource -> count. + * Eg: + * MATCH + * AUTO - 2 + * MANUAL - 1 + * NO_MATCH + * AUTO - 1 + * MANUAL - 3 + */ + @JsonProperty("matchResult2linkSource2count") + private Map> myMatchTypeToLinkToCountMap; + + /** + * Score buckets (in brackets of 0.01 size, and null) to counts. + */ + @JsonProperty("scoreCounts") + private Map myScoreCounts; + + /** + * The number of golden resources. + */ + @JsonProperty("goldenResources") + private long myGoldenResourcesCount; + + /** + * The number of source resources. + */ + @JsonProperty("sourceResources") + private long mySourceResourcesCount; + + /** + * The number of excluded resources. + * These are necessarily a subset of both + * GoldenResources and SourceResources + * (as each Blocked resource will still generate + * a GoldenResource) + */ + @JsonProperty("excludedResources") + private long myExcludedResources; + + public String getResourceType() { + return myResourceType; + } + + public Map> getMatchTypeToLinkToCountMap() { + if (myMatchTypeToLinkToCountMap == null) { + myMatchTypeToLinkToCountMap = new HashMap<>(); + } + return myMatchTypeToLinkToCountMap; + } + + public void addMetric( + MdmMatchResultEnum theMdmMatchResultEnum, MdmLinkSourceEnum theLinkSourceEnum, long theCount) { + Map> map = getMatchTypeToLinkToCountMap(); + + if (!map.containsKey(theMdmMatchResultEnum)) { + map.put(theMdmMatchResultEnum, new HashMap<>()); + } + Map lsToCountMap = map.get(theMdmMatchResultEnum); + lsToCountMap.put(theLinkSourceEnum, theCount); + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public long getGoldenResourcesCount() { + return myGoldenResourcesCount; + } + + public void setGoldenResourcesCount(long theGoldenResourcesCount) { + myGoldenResourcesCount = theGoldenResourcesCount; + } + + public long getSourceResourcesCount() { + return mySourceResourcesCount; + } + + public void setSourceResourcesCount(long theSourceResourcesCount) { + mySourceResourcesCount = theSourceResourcesCount; + } + + public long getExcludedResources() { + return myExcludedResources; + } + + public void setExcludedResources(long theExcludedResources) { + myExcludedResources = theExcludedResources; + } + + public Map getScoreCounts() { + if (myScoreCounts == null) { + myScoreCounts = new LinkedHashMap<>(); + } + return myScoreCounts; + } + + public void addScore(String theScore, Long theCount) { + getScoreCounts().put(theScore, theCount); + } + + public static MdmMetrics fromSeperableMetrics( + MdmResourceMetrics theMdmResourceMetrics, + MdmLinkMetrics theLinkMetrics, + MdmLinkScoreMetrics theLinkScoreMetrics) { + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType(theMdmResourceMetrics.getResourceType()); + metrics.setExcludedResources(theMdmResourceMetrics.getExcludedResources()); + metrics.setGoldenResourcesCount(theMdmResourceMetrics.getGoldenResourcesCount()); + metrics.setSourceResourcesCount(theMdmResourceMetrics.getSourceResourcesCount()); + metrics.myMatchTypeToLinkToCountMap = theLinkMetrics.getMatchTypeToLinkToCountMap(); + metrics.myScoreCounts = theLinkScoreMetrics.getScoreCounts(); + return metrics; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java new file mode 100644 index 00000000000..49939a5fd27 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java @@ -0,0 +1,60 @@ +package ca.uhn.fhir.mdm.model; + +public class MdmResourceMetrics { + + /** + * The resource type to which these metrics apply. + */ + private String myResourceType; + + /** + * The number of golden resources. + */ + private long myGoldenResourcesCount; + + /** + * The number of source resources. + */ + private long mySourceResourcesCount; + + /** + * The number of excluded resources. + * These are necessarily a subset of both + * GoldenResources and SourceResources + * (as each Blocked resource will still generate + * a GoldenResource) + */ + private long myExcludedResources; + + public String getResourceType() { + return myResourceType; + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public long getGoldenResourcesCount() { + return myGoldenResourcesCount; + } + + public void setGoldenResourcesCount(long theGoldenResourcesCount) { + myGoldenResourcesCount = theGoldenResourcesCount; + } + + public long getSourceResourcesCount() { + return mySourceResourcesCount; + } + + public void setSourceResourcesCount(long theSourceResourcesCount) { + mySourceResourcesCount = theSourceResourcesCount; + } + + public long getExcludedResources() { + return myExcludedResources; + } + + public void setExcludedResources(long theExcludedResources) { + myExcludedResources = theExcludedResources; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index a2ca1b94930..17613f59be8 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -49,6 +49,12 @@ public class MdmTransactionContext { private String myResourceType; + /** + * Whether or not the currently processed resource is a 'blocked resource'. + * This will only be set on matching. + */ + private boolean myIsBlockedResource; + private List myMdmLinkEvents = new ArrayList<>(); public TransactionLogMessages getTransactionLogMessages() { @@ -111,4 +117,12 @@ public class MdmTransactionContext { public void setMdmLinks(List theMdmLinkEvents) { myMdmLinkEvents = theMdmLinkEvents; } + + public void setIsBlocked(boolean theIsBlocked) { + myIsBlockedResource = theIsBlocked; + } + + public boolean getIsBlocked() { + return myIsBlockedResource; + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java index d133a7a9605..c7b46f4cd38 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java @@ -116,6 +116,14 @@ public class GoldenResourceHelper { MdmResourceUtil.setMdmManaged(newGoldenResource); MdmResourceUtil.setGoldenResource(newGoldenResource); + // TODO - on updating links, if resolving a link, this should go away? + // blocked resource's golden resource will be marked special + // they are not part of MDM matching algorithm (will not link to other resources) + // but other resources can link to them + if (theMdmTransactionContext.getIsBlocked()) { + MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource(newGoldenResource); + } + // add the partition id to the new resource newGoldenResource.setUserData( Constants.RESOURCE_PARTITION_ID, diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java index 44f34ea094f..4e7faf9d523 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java @@ -132,6 +132,24 @@ public final class MdmResourceUtil { MdmConstants.DISPLAY_GOLDEN_REDIRECT); } + /** + * Adds the BLOCKED tag to the golden resource. + * Because this is called *before* a resource is saved, + * we must add a new system/code combo to it + * @param theBaseResource + * @return + */ + public static IBaseResource setGoldenResourceAsBlockedResourceGoldenResource(IBaseResource theBaseResource) { + IBaseCoding tag = theBaseResource.getMeta().addTag(); + tag.setSystem(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS); + tag.setCode(MdmConstants.CODE_BLOCKED); + tag.setDisplay(MdmConstants.CODE_BLOCKED_DISPLAY); + tag.setUserSelected(false); + tag.setVersion("1"); + + return theBaseResource; + } + /** * WARNING: This code may _look_ like it replaces in place a code of a tag, but this DOES NOT ACTUALLY WORK!. In reality what will * happen is a secondary tag will be created with the same system. the only way to actually remove a tag from a resource diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java new file mode 100644 index 00000000000..d2085329296 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java @@ -0,0 +1,51 @@ +package ca.uhn.fhir.mdm.util; + +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.MdmConstants; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenParam; + +public class MdmSearchParamBuildingUtils { + + private static final String IDENTIFIER = "identifier"; + + private static final String TAG = "_tag"; + + /** + * Builds a search parameter map that can be used to find the + * golden resources associated with MDM blocked resources (ie, those + * resources that were omitted from MDM matching). + */ + public static SearchParameterMap buildSearchParameterForBlockedResourceCount(String theResourceType) { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + TokenAndListParam tagsToSearch = new TokenAndListParam(); + tagsToSearch.addAnd(new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); + tagsToSearch.addAnd(new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_BLOCKED)); + + map.add(TAG, tagsToSearch); + return map; + } + + /** + * Creates a SearchParameterMap used for searching for golden resources + * by EID specifically. + */ + public static SearchParameterMap buildEidSearchParameterMap( + String theEid, String theResourceType, MdmRulesJson theMdmRules) { + SearchParameterMap map = buildBasicGoldenResourceSearchParameterMap(theEid); + map.add(IDENTIFIER, new TokenParam(theMdmRules.getEnterpriseEIDSystemForResourceType(theResourceType), theEid)); + return map; + } + + /** + * Creates a SearchParameterMap that can be used to find golden resources. + */ + public static SearchParameterMap buildBasicGoldenResourceSearchParameterMap(String theResourceType) { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(TAG, new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); + return map; + } +} diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java index 8b128896f1d..85671682cc0 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java @@ -1,11 +1,21 @@ package ca.uhn.fhir.mdm.util; +import ca.uhn.fhir.mdm.api.MdmConstants; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class MdmResourceUtilTest { @@ -20,4 +30,28 @@ class MdmResourceUtilTest { assertThat(hasGoldenRecordTag, is(equalTo(false))); } + + @Test + public void testSetGoldenAndBlockedResource() { + // setup + Patient patient = new Patient(); + patient.setActive(true); + + // test + Patient changed = (Patient) MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource( + MdmResourceUtil.setGoldenResource(patient) + ); + + // verify + assertNotNull(changed); + List tags = changed.getMeta().getTag(); + Set codes = new HashSet<>(); + codes.add(MdmConstants.CODE_BLOCKED); + codes.add(MdmConstants.CODE_GOLDEN_RECORD); + assertEquals(2, tags.size()); + for (Coding code : tags) { + assertEquals(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, code.getSystem()); + assertTrue(codes.contains(code.getCode())); + } + } } diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 97a9d442082..c57d846a2d1 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 05f1e323a7c..a52010ad2d0 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 3d61960b0b0..7a91609e596 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 - 6.9.8-SNAPSHOT + 6.9.9-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 b3446ed9a2a..a3b872bcbb6 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index ab639b80f69..31a240fdbb3 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 - 6.9.8-SNAPSHOT + 6.9.9-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 bd046a5d4a5..119a3ced85c 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index c465731f954..4d50f71fa86 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.8-SNAPSHOT + 6.9.9-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 3cd4f85ea30..148c5717fb5 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 - 6.9.8-SNAPSHOT + 6.9.9-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 36d820541cf..debccca5b4b 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 - 6.9.8-SNAPSHOT + 6.9.9-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 cdf05f46650..9b6ba0cdf17 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 - 6.9.8-SNAPSHOT + 6.9.9-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 27dc8f1ee62..05465b1fd32 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 - 6.9.8-SNAPSHOT + 6.9.9-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 7f01939e11a..8d89550158d 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 - 6.9.8-SNAPSHOT + 6.9.9-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 59b94d03c74..6a845cc9ffc 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 465d0b784d3..16232047a4f 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 50f71fb0834..f7ee91e88a1 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index da9f668f778..53aaee618a2 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 4b55a6d0612..259a89b3dbb 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 1e2ba7f7e8c..791bfc09bbf 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index bceff64f591..6adae32a21c 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index b815a275b9f..0ba86977969 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index e665b690315..b12e654faf8 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index bb9a01ab472..8677b904bbe 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-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 6f9ae35ffb8..23a3f4ccfd9 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index a9643757fc7..e3d347cc12c 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index cda18bff14a..1fb57ba2778 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 - 6.9.8-SNAPSHOT + 6.9.9-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 e0567ffd108..98b3407c903 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index e070a8fb9e7..db74917e705 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 5b9471961cb..b31d930227f 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index c97208fe2e2..eab80fc01a3 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java index c8cdbf8062f..239efa1efea 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/fhirpath/FhirPathR5.java @@ -16,7 +16,7 @@ import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.TypeDetails; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRPathEngine; -import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r5.utils.FHIRPathUtilityClasses; import java.util.List; import java.util.Optional; @@ -108,7 +108,7 @@ public class FhirPathR5 implements IFhirPath { } @Override - public FunctionDetails resolveFunction(String functionName) { + public FHIRPathUtilityClasses.FunctionDetails resolveFunction(String functionName) { return null; } diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index f208de1b2f7..3c9b6e6c687 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index fb872aa9377..d1336006d52 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 77f87de8553..82f33474782 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 - 6.9.8-SNAPSHOT + 6.9.9-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 0c33abe7f82..7ed3464e804 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 - 6.9.8-SNAPSHOT + 6.9.9-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 1d399034981..8ec6bcb529e 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 - 6.9.8-SNAPSHOT + 6.9.9-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 fb0365de8a7..961ea491248 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 - 6.9.8-SNAPSHOT + 6.9.9-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 8d78cb03be9..1b4da7b170b 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 - 6.9.8-SNAPSHOT + 6.9.9-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 5018a5c0a0c..f44873e9e6a 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index f7c7a9fcfa1..d11066607d5 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 2880a0397e4..0ec080fa1e6 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 133b6269e46..01786c9d945 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index a9bf58d73ad..4c37982117a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.9.8-SNAPSHOT + 6.9.9-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 3d54ad5b0c4..95bb4d038b7 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 - 6.9.8-SNAPSHOT + 6.9.9-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 11f932aa409..bd679c2e152 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 - 6.9.8-SNAPSHOT + 6.9.9-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 2bac2e98877..b5570d3c637 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 - 6.9.8-SNAPSHOT + 6.9.9-SNAPSHOT ../../pom.xml From 5411622dee45835601c9f9c9d0bff31b8a3988d8 Mon Sep 17 00:00:00 2001 From: JasonRoberts-smile <85363818+JasonRoberts-smile@users.noreply.github.com> Date: Mon, 2 Oct 2023 09:23:42 -0400 Subject: [PATCH 05/86] add exclusions to terser reference method (#5343) --- .../java/ca/uhn/fhir/util/FhirTerser.java | 41 +++++++++++++++ .../ca/uhn/fhir/util/FhirTerserR4Test.java | 50 +++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index 4e4319076c3..294761ae174 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -287,9 +287,33 @@ public class FhirTerser { return retVal; } + /** + * Extracts all outbound references from a resource + * + * @param theResource the resource to be analyzed + * @return a list of references to other resources + */ public List getAllResourceReferences(final IBaseResource theResource) { + return getAllResourceReferencesExcluding(theResource, Lists.newArrayList()); + } + + /** + * Extracts all outbound references from a resource, excluding any that are located on black-listed parts of the + * resource + * + * @param theResource the resource to be analyzed + * @param thePathsToExclude a list of dot-delimited paths not to include in the result + * @return a list of references to other resources + */ + public List getAllResourceReferencesExcluding( + final IBaseResource theResource, List thePathsToExclude) { final ArrayList retVal = new ArrayList<>(); BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); + List> tokenizedPathsToExclude = thePathsToExclude.stream() + .map(path -> StringUtils.split(path, ".")) + .map(Lists::newArrayList) + .collect(Collectors.toList()); + visit(newMap(), theResource, theResource, null, null, def, new IModelVisitor() { @Override public void acceptElement( @@ -301,6 +325,10 @@ public class FhirTerser { if (theElement == null || theElement.isEmpty()) { return; } + + if (thePathToElement != null && pathShouldBeExcluded(tokenizedPathsToExclude, thePathToElement)) { + return; + } if (IBaseReference.class.isAssignableFrom(theElement.getClass())) { retVal.add(new ResourceReferenceInfo( myContext, theOuterResource, thePathToElement, (IBaseReference) theElement)); @@ -310,6 +338,19 @@ public class FhirTerser { return retVal; } + private boolean pathShouldBeExcluded(List> theTokenizedPathsToExclude, List thePathToElement) { + return theTokenizedPathsToExclude.stream().anyMatch(p -> { + // Check whether the path to the element starts with the path to be excluded + if (p.size() > thePathToElement.size()) { + return false; + } + + List prefix = thePathToElement.subList(0, p.size()); + + return Objects.equals(p, prefix); + }); + } + private BaseRuntimeChildDefinition getDefinition( BaseRuntimeElementCompositeDefinition theCurrentDef, List theSubList) { BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0)); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java index 3aba95d075a..5182d2491a9 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/FhirTerserR4Test.java @@ -12,6 +12,7 @@ import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Bundle; @@ -32,6 +33,7 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient.LinkType; import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.Provenance; import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.ResourceType; @@ -511,6 +513,54 @@ public class FhirTerserR4Test { } + @Test + public void testGetAllResourceReferences() { + // setup + Provenance p = new Provenance(); + p.addTarget(new Reference("Observation/1")); + p.addTarget(new Reference("Observation/2")); + p.setLocation(new Reference("Location/3")); + p.getAgentFirstRep().setWho(new Reference("Practitioner/4")); + p.getAgentFirstRep().setOnBehalfOf(new Reference("Organization/5")); + p.getEntityFirstRep().setWhat(new Reference("DocumentReference/6")); + + // execute + FhirTerser t = myCtx.newTerser(); + List references = t.getAllResourceReferences(p); + + // validate + assertEquals(6, references.size()); + assertThat(toResourceIds(references), containsInAnyOrder("Observation/1", "Observation/2", "Location/3", "Practitioner/4", "Organization/5", "DocumentReference/6")); + } + + @Test + public void testGetAllResourceReferencesExcluding() { + // setup + Provenance p = new Provenance(); + p.addTarget(new Reference("Observation/1")); + p.addTarget(new Reference("Observation/2")); + p.setLocation(new Reference("Location/3")); + p.getAgentFirstRep().setWho(new Reference("Practitioner/4")); + p.getAgentFirstRep().setOnBehalfOf(new Reference("Organization/5")); + p.getEntityFirstRep().setWhat(new Reference("DocumentReference/6")); + + // execute + FhirTerser t = myCtx.newTerser(); + List references = t.getAllResourceReferencesExcluding(p, List.of("target")); + + // validate + assertEquals(4, references.size()); + assertThat(toResourceIds(references), containsInAnyOrder("Location/3", "Practitioner/4", "Organization/5", "DocumentReference/6")); + } + + private List toResourceIds(List references) { + return references.stream() + .map(ResourceReferenceInfo::getResourceReference) + .map(IBaseReference::getReferenceElement) + .map(IIdType::getValue) + .collect(Collectors.toList()); + } + @Test public void testGetResourceReferenceInExtension() { Patient p = new Patient(); From 4e9bd153cf1ab11192bd3af4db17eea3de55c7a1 Mon Sep 17 00:00:00 2001 From: StevenXLi Date: Mon, 2 Oct 2023 11:32:58 -0400 Subject: [PATCH 06/86] 5339 bulk export errors when patient compartment searchparameter of the resource is not present (#5340) * added failing test * implemented solution, and fixed test * added changelog * fixed formatting * fixed test not passing * fixed test and formatting * added warning for failing the active search param check * code review change --------- Co-authored-by: Steven Li --- ...ameter-of-the-resource-is-not-present.yaml | 6 + .../export/svc/JpaBulkExportProcessor.java | 70 +++++++---- .../jpa/search/builder/SearchBuilder.java | 2 +- .../svc/JpaBulkExportProcessorTest.java | 9 ++ .../uhn/fhir/jpa/bulk/BulkDataExportTest.java | 109 ++++++++++++++++-- 5 files changed, 167 insertions(+), 29 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml new file mode 100644 index 00000000000..d0e0461aeb4 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5339 +jira: SMILE-7236 +title: "Previously, Bulk Export will error when processing a resource if the patient compartment SearchParameter of that resource is not present. +This has been fixed, the new behaviour is to ignore such resources." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java index ae0fb1115f1..8245fdaf066 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java @@ -53,6 +53,7 @@ import ca.uhn.fhir.rest.param.HasOrListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.Logs; @@ -121,6 +122,9 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { @Autowired private IHapiTransactionService myHapiTransactionService; + @Autowired + private ISearchParamRegistry mySearchParamRegistry; + private IFhirPath myFhirPath; @Override @@ -298,28 +302,36 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { private LinkedHashSet getRelatedResourceTypePids( ExportPIDIteratorParameters theParams, RuntimeResourceDefinition theDef) throws IOException { LinkedHashSet pids = new LinkedHashSet<>(); - // expand the group pid -> list of patients in that group (list of patient pids) - Set expandedMemberResourceIds = expandAllPatientPidsFromGroup(theParams); - assert !expandedMemberResourceIds.isEmpty(); - Logs.getBatchTroubleshootingLog() - .debug("{} has been expanded to members:[{}]", theParams.getGroupId(), expandedMemberResourceIds); + // Check if the patient compartment search parameter is active to enable export of this resource + RuntimeSearchParam activeSearchParam = + getActivePatientSearchParamForCurrentResourceType(theParams.getResourceType()); + if (activeSearchParam != null) { + // expand the group pid -> list of patients in that group (list of patient pids) + Set expandedMemberResourceIds = expandAllPatientPidsFromGroup(theParams); + assert !expandedMemberResourceIds.isEmpty(); + Logs.getBatchTroubleshootingLog() + .debug("{} has been expanded to members:[{}]", theParams.getGroupId(), expandedMemberResourceIds); - // for each patient pid -> - // search for the target resources, with their correct patient references, chunked. - // The results will be jammed into myReadPids - QueryChunker queryChunker = new QueryChunker<>(); - queryChunker.chunk(expandedMemberResourceIds, QUERY_CHUNK_SIZE, (idChunk) -> { - try { - queryResourceTypeWithReferencesToPatients(pids, idChunk, theParams, theDef); - } catch (IOException ex) { - // we will never see this; - // SearchBuilder#QueryIterator does not (nor can ever) throw - // an IOException... but Java requires the check, - // so we'll put a log here (just in the off chance) - ourLog.error("Couldn't close query iterator ", ex); - throw new RuntimeException(Msg.code(2346) + "Couldn't close query iterator", ex); - } - }); + // for each patient pid -> + // search for the target resources, with their correct patient references, chunked. + // The results will be jammed into myReadPids + QueryChunker queryChunker = new QueryChunker<>(); + queryChunker.chunk(expandedMemberResourceIds, QUERY_CHUNK_SIZE, (idChunk) -> { + try { + queryResourceTypeWithReferencesToPatients(pids, idChunk, theParams, theDef); + } catch (IOException ex) { + // we will never see this; + // SearchBuilder#QueryIterator does not (nor can ever) throw + // an IOException... but Java requires the check, + // so we'll put a log here (just in the off chance) + ourLog.error("Couldn't close query iterator ", ex); + throw new RuntimeException(Msg.code(2346) + "Couldn't close query iterator", ex); + } + }); + } else { + ourLog.warn("No active patient compartment search parameter(s) for resource type " + + theParams.getResourceType()); + } return pids; } @@ -600,6 +612,22 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { } } + private RuntimeSearchParam getActivePatientSearchParamForCurrentResourceType(String theResourceType) { + String activeSearchParamName = ""; + String resourceToCheck = theResourceType; + if (!PATIENT_BULK_EXPORT_FORWARD_REFERENCE_RESOURCE_TYPES.contains(theResourceType)) { + activeSearchParamName = + getPatientSearchParamForCurrentResourceType(theResourceType).getName(); + } else if ("Practitioner".equalsIgnoreCase(theResourceType)) { + resourceToCheck = "Patient"; + activeSearchParamName = "general-practitioner"; + } else if ("Organization".equalsIgnoreCase(theResourceType)) { + resourceToCheck = "Patient"; + activeSearchParamName = "organization"; + } + return mySearchParamRegistry.getActiveSearchParam(resourceToCheck, activeSearchParamName); + } + /** * Must not be called for resources types listed in PATIENT_BULK_EXPORT_FORWARD_REFERENCE_RESOURCE_TYPES * diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index 3c7f7613ceb..60d0a986031 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -1400,7 +1400,7 @@ public class SearchBuilder implements ISearchBuilder { q.setMaxResults(maxCount); } if (hasDesiredResourceTypes) { - q.setParameter("desired_target_resource_types", String.join(", ", desiredResourceTypes)); + q.setParameter("desired_target_resource_types", desiredResourceTypes); } List results = q.getResultList(); for (Object nextRow : results) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java index 0382404fb38..9822399a55e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.bulk.export.svc; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -23,12 +24,15 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; @@ -139,6 +143,9 @@ public class JpaBulkExportProcessorTest { @Mock private MdmExpansionCacheSvc myMdmExpansionCacheSvc; + @Mock + private ISearchParamRegistry mySearchParamRegistry; + @Spy private IHapiTransactionService myTransactionService = new NonTransactionalHapiTransactionService(); @@ -409,6 +416,8 @@ public class JpaBulkExportProcessorTest { ISearchBuilder observationSearchBuilder = mock(ISearchBuilder.class); // when + RuntimeSearchParam searchParam = new RuntimeSearchParam(new IdType("1"), "", "", "", "", RestSearchParameterTypeEnum.STRING, Collections.singleton(""), Collections.singleton(""), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, Collections.singleton("")); + when(mySearchParamRegistry.getActiveSearchParam(any(), any())).thenReturn(searchParam); // expandAllPatientPidsFromGroup when(myDaoRegistry.getResourceDao(eq("Group"))) .thenReturn(groupDao); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java index 808c8ee6736..72b994d4703 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java @@ -12,12 +12,16 @@ import ca.uhn.fhir.jpa.api.model.BulkExportJobResults; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RequestTypeEnum; +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.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.client.apache.ResourceEntity; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.test.utilities.HttpClientExtension; @@ -50,6 +54,7 @@ import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Provenance; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.ServiceRequest; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; @@ -149,6 +154,99 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { verifyBulkExportResults(options, Collections.singletonList("Patient/PF"), Collections.singletonList("Patient/PM")); } + @Test + public void testGroupBulkExportWithMissingObservationSearchParams() { + mySearchParameterDao.update(createDisabledObservationPatientSearchParameter(), mySrd); + mySearchParamRegistry.forceRefresh(); + + // Create some resources + Patient patient = new Patient(); + patient.setId("PF"); + patient.setGender(Enumerations.AdministrativeGender.FEMALE); + patient.setActive(true); + myClient.update().resource(patient).execute(); + + patient = new Patient(); + patient.setId("PM"); + patient.setGender(Enumerations.AdministrativeGender.MALE); + patient.setActive(true); + myClient.update().resource(patient).execute(); + + Group group = new Group(); + group.setId("Group/G"); + group.setActive(true); + group.addMember().getEntity().setReference("Patient/PF"); + group.addMember().getEntity().setReference("Patient/PM"); + myClient.update().resource(group).execute(); + + Observation observation = new Observation(); + observation.setStatus(Observation.ObservationStatus.AMENDED); + observation.setSubject(new Reference("Patient/PF")); + String obsId = myClient.create().resource(observation).execute().getId().toUnqualifiedVersionless().getValue(); + + // set the export options + BulkExportJobParameters options = new BulkExportJobParameters(); + options.setResourceTypes(Sets.newHashSet("Patient", "Observation")); + options.setGroupId("Group/G"); + options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); + options.setOutputFormat(Constants.CT_FHIR_NDJSON); + verifyBulkExportResults(options, List.of("Patient/PF", "Patient/PM"), Collections.singletonList(obsId)); + } + + @Test + public void testGroupBulkExportWithMissingPatientSearchParams() { + mySearchParameterDao.update(createDisabledPatientPractitionerSearchParameter(), mySrd); + mySearchParamRegistry.forceRefresh(); + + Practitioner practitioner = new Practitioner(); + practitioner.setActive(true); + String practId = myClient.create().resource(practitioner).execute().getId().toUnqualifiedVersionless().getValue(); + + // Create some resources + Patient patient = new Patient(); + patient.setId("P1"); + patient.setActive(true); + patient.addGeneralPractitioner().setReference(practId); + myClient.update().resource(patient).execute(); + + Group group = new Group(); + group.setId("Group/G"); + group.setActive(true); + group.addMember().getEntity().setReference("Patient/P1"); + myClient.update().resource(group).execute(); + + // set the export options + BulkExportJobParameters options = new BulkExportJobParameters(); + options.setResourceTypes(Sets.newHashSet("Patient", "Practitioner")); + options.setGroupId("Group/G"); + options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); + options.setOutputFormat(Constants.CT_FHIR_NDJSON); + verifyBulkExportResults(options, Collections.singletonList("Patient/P1"), Collections.singletonList(practId)); + } + + private SearchParameter createDisabledObservationPatientSearchParameter() { + SearchParameter observation_patient = new SearchParameter(); + observation_patient.setId("clinical-patient"); + observation_patient.addBase("Observation"); + observation_patient.setStatus(Enumerations.PublicationStatus.RETIRED); + observation_patient.setCode("patient"); + observation_patient.setType(Enumerations.SearchParamType.REFERENCE); + observation_patient.addTarget("Patient"); + observation_patient.setExpression("Observation.subject.where(resolve() is Patient)"); + return observation_patient; + } + + private SearchParameter createDisabledPatientPractitionerSearchParameter() { + SearchParameter patient_practitioner = new SearchParameter(); + patient_practitioner.setId("Patient-general-practitioner"); + patient_practitioner.addBase("Patient"); + patient_practitioner.setStatus(Enumerations.PublicationStatus.RETIRED); + patient_practitioner.setCode("general-practitioner"); + patient_practitioner.setType(Enumerations.SearchParamType.REFERENCE); + patient_practitioner.addTarget("Practitioner"); + patient_practitioner.setExpression("Patient.generalPractitioner"); + return patient_practitioner; + } @Test public void testGroupBulkExportWithTypeFilter_OnTags_InlineTagMode() { @@ -446,12 +544,12 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Encounter")); + options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Practitioner", "Organization")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); options.setOutputFormat(Constants.CT_FHIR_NDJSON); - verifyBulkExportResults(options, List.of("Patient/P1", practId, orgId, encId, encId2, locId), List.of("Patient/P2", orgId2, encId3, locId2)); + verifyBulkExportResults(options, List.of("Patient/P1", practId, orgId, encId, encId2), List.of("Patient/P2", orgId2, encId3)); } @Test @@ -487,7 +585,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Observation")); + options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Observation", "Practitioner")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); @@ -544,7 +642,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Observation", "Provenance")); + options.setResourceTypes(Sets.newHashSet("Patient", "Observation", "Provenance", "Device")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); @@ -944,10 +1042,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { } catch (IOException e) { fail(e.toString()); } - } - - return jobInstance; } for (String containedString : theContainedList) { From 743e2c178b2a7f6212e537270114d4428b7a9558 Mon Sep 17 00:00:00 2001 From: Brenin Rhodes Date: Tue, 3 Oct 2023 05:15:32 -0600 Subject: [PATCH 07/86] Add Registries for CDS CR Services (#5342) * Add registries for CdsCrServices and CrDiscoveryServices to allow registration of custom services * Add changelog * Fix broken find method * Fix crDiscoveryServiceFactory --- .../7_0_0/5341-add-cds-cr-registries.yaml | 4 + .../fhir/cdshooks/config/CdsHooksConfig.java | 87 +++++++++++++------ .../cdshooks/svc/cr/CdsCrServiceRegistry.java | 51 +++++++++++ .../svc/cr/ICdsCrServiceRegistry.java | 33 +++++++ .../CdsCrDiscoveryServiceRegistry.java | 53 +++++++++++ .../ICdsCrDiscoveryServiceRegistry.java | 34 ++++++++ 6 files changed, 236 insertions(+), 26 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml new file mode 100644 index 00000000000..bda264324e3 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5341 +title: "Added registries for CdsCrServices and CrDiscoveryServices in CDS Hooks to allow registration of custom services." diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java index f5bd894f645..b0c115807ee 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java @@ -34,28 +34,36 @@ import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceDstu3; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR5; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceRegistry; import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsServiceInterceptor; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrService; import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceDstu3; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceR4; -import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CrDiscoveryServiceR5; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CdsCrDiscoveryServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICdsCrDiscoveryServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryService; import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchDaoSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchFhirClientSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsResolutionStrategySvc; import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.fhir.api.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; + @Configuration public class CdsHooksConfig { + private static final Logger ourLog = LoggerFactory.getLogger(CdsHooksConfig.class); public static final String CDS_HOOKS_OBJECT_MAPPER_FACTORY = "cdsHooksObjectMapperFactory"; @@ -97,7 +105,15 @@ public class CdsHooksConfig { } @Bean - public ICdsCrServiceFactory cdsCrServiceFactory(FhirContext theFhirContext, ICdsConfigService theCdsConfigService) { + public ICdsCrServiceRegistry cdsCrServiceRegistry() { + return new CdsCrServiceRegistry(); + } + + @Bean + public ICdsCrServiceFactory cdsCrServiceFactory( + FhirContext theFhirContext, + ICdsConfigService theCdsConfigService, + ICdsCrServiceRegistry theCdsCrServiceRegistry) { return id -> { if (myRepositoryFactory == null) { return null; @@ -105,22 +121,35 @@ public class CdsHooksConfig { RequestDetails rd = theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); Repository repository = myRepositoryFactory.create(rd); - switch (theFhirContext.getVersion().getVersion()) { - case DSTU3: - return new CdsCrServiceDstu3(rd, repository); - case R4: - return new CdsCrServiceR4(rd, repository); - case R5: - return new CdsCrServiceR5(rd, repository); - default: - return null; + Optional> clazz = + theCdsCrServiceRegistry.find(theFhirContext.getVersion().getVersion()); + if (clazz.isEmpty()) { + return null; + } + try { + Constructor constructor = + clazz.get().getConstructor(RequestDetails.class, Repository.class); + return constructor.newInstance(rd, repository); + } catch (NoSuchMethodException + | InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + ourLog.error("Error encountered attempting to construct the CdsCrService: " + e.getMessage()); + return null; } }; } + @Bean + public ICdsCrDiscoveryServiceRegistry cdsCrDiscoveryServiceRegistry() { + return new CdsCrDiscoveryServiceRegistry(); + } + @Bean public ICrDiscoveryServiceFactory crDiscoveryServiceFactory( - FhirContext theFhirContext, ICdsConfigService theCdsConfigService) { + FhirContext theFhirContext, + ICdsConfigService theCdsConfigService, + ICdsCrDiscoveryServiceRegistry theCdsCrDiscoveryServiceRegistry) { return id -> { if (myRepositoryFactory == null) { return null; @@ -128,15 +157,21 @@ public class CdsHooksConfig { RequestDetails rd = theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); Repository repository = myRepositoryFactory.create(rd); - switch (theFhirContext.getVersion().getVersion()) { - case DSTU3: - return new CrDiscoveryServiceDstu3(rd.getId(), repository); - case R4: - return new CrDiscoveryServiceR4(rd.getId(), repository); - case R5: - return new CrDiscoveryServiceR5(rd.getId(), repository); - default: - return null; + Optional> clazz = theCdsCrDiscoveryServiceRegistry.find( + theFhirContext.getVersion().getVersion()); + if (clazz.isEmpty()) { + return null; + } + try { + Constructor constructor = + clazz.get().getConstructor(IIdType.class, Repository.class); + return constructor.newInstance(rd.getId(), repository); + } catch (NoSuchMethodException + | InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + ourLog.error("Error encountered attempting to construct the CrDiscoveryService: " + e.getMessage()); + return null; } }; } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java new file mode 100644 index 00000000000..f8804ede21e --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java @@ -0,0 +1,51 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nonnull; + +public class CdsCrServiceRegistry implements ICdsCrServiceRegistry { + private final Map> myCdsCrServices; + + public CdsCrServiceRegistry() { + myCdsCrServices = new HashMap<>(); + myCdsCrServices.put(FhirVersionEnum.DSTU3, CdsCrServiceDstu3.class); + myCdsCrServices.put(FhirVersionEnum.R4, CdsCrServiceR4.class); + myCdsCrServices.put(FhirVersionEnum.R5, CdsCrServiceR5.class); + } + + public void register( + @Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class theCdsCrService) { + myCdsCrServices.put(theFhirVersion, theCdsCrService); + } + + public void unregister(@Nonnull FhirVersionEnum theFhirVersion) { + myCdsCrServices.remove(theFhirVersion); + } + + public Optional> find(@Nonnull FhirVersionEnum theFhirVersion) { + return Optional.ofNullable(myCdsCrServices.get(theFhirVersion)); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java new file mode 100644 index 00000000000..10969ae138e --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java @@ -0,0 +1,33 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; + +import java.util.Optional; +import javax.annotation.Nonnull; + +public interface ICdsCrServiceRegistry { + void register(@Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class theCdsCrService); + + void unregister(@Nonnull FhirVersionEnum theFhirVersion); + + Optional> find(@Nonnull FhirVersionEnum theFhirVersion); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java new file mode 100644 index 00000000000..a62a17e494d --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java @@ -0,0 +1,53 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nonnull; + +public class CdsCrDiscoveryServiceRegistry implements ICdsCrDiscoveryServiceRegistry { + private final Map> myCrDiscoveryServices; + + public CdsCrDiscoveryServiceRegistry() { + myCrDiscoveryServices = new HashMap<>(); + myCrDiscoveryServices.put(FhirVersionEnum.DSTU3, CrDiscoveryServiceDstu3.class); + myCrDiscoveryServices.put(FhirVersionEnum.R4, CrDiscoveryServiceR4.class); + myCrDiscoveryServices.put(FhirVersionEnum.R5, CrDiscoveryServiceR5.class); + } + + public void register( + @Nonnull FhirVersionEnum theFhirVersion, + @Nonnull Class theCrDiscoveryService) { + myCrDiscoveryServices.put(theFhirVersion, theCrDiscoveryService); + } + + public void unregister(@Nonnull FhirVersionEnum theFhirVersion) { + myCrDiscoveryServices.remove(theFhirVersion); + } + + @Override + public Optional> find(@Nonnull FhirVersionEnum theFhirVersion) { + return Optional.ofNullable(myCrDiscoveryServices.get(theFhirVersion)); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java new file mode 100644 index 00000000000..6c5e2c093fb --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java @@ -0,0 +1,34 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; + +import java.util.Optional; +import javax.annotation.Nonnull; + +public interface ICdsCrDiscoveryServiceRegistry { + void register( + @Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class ICrDiscoveryService); + + void unregister(@Nonnull FhirVersionEnum theFhirVersion); + + Optional> find(@Nonnull FhirVersionEnum theFhirVersion); +} From e5f700fc21cc19ce0a5a1c0da2dfc797e75895d5 Mon Sep 17 00:00:00 2001 From: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Date: Thu, 5 Oct 2023 12:40:16 -0400 Subject: [PATCH 08/86] 5344 $expunge operation fails when the operation is executed on a specific partition (#5347) * initial failing test. * solution with changelog * fixing pom dependency version * fixing pom circular dependency issue and making the new wrapper class generic. * Fixing tests assertion. * Moving test to solve dependency issues. * addressing code review comments. --------- Co-authored-by: peartree --- ...e-failing-when-executing-on-partition.yaml | 5 + .../fhir/jpa/dao/tx/ExpungeOperationTest.java | 91 +++++++++++++++++++ .../dao/tx/PartitionAwareSupplierTest.java | 67 ++++++++++++++ .../jpa/svc/MockHapiTransactionService.java | 2 +- .../jpa/dao/expunge/ExpungeOperation.java | 43 ++++++--- .../dao/expunge/PartitionAwareSupplier.java | 27 ++++++ .../jpa/dao/tx/HapiTransactionService.java | 5 + 7 files changed, 227 insertions(+), 13 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml new file mode 100644 index 00000000000..61ffdfbdaf6 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5344 +jira: SMILE-7324 +title: "Previously, issuing an expunge operation for resources on a specific partition would fail. This problem has been fixed." diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java new file mode 100644 index 00000000000..1ef53f61b23 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java @@ -0,0 +1,91 @@ +package ca.uhn.fhir.jpa.dao.tx; + +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.api.model.ExpungeOptions; +import ca.uhn.fhir.jpa.dao.expunge.ExpungeOperation; +import ca.uhn.fhir.jpa.dao.expunge.IResourceExpungeService; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ExpungeOperationTest { + + @Captor + private ArgumentCaptor myBuilderArgumentCaptor; + @Spy + private MockHapiTransactionService myHapiTransactionService; + private JpaStorageSettings myStorageSettings; + @Mock + private IResourceExpungeService myIResourceExpungeService; + private static final String ourExpectedTenantId = "TenantA"; + + @BeforeEach + public void beforeEach(){ + myStorageSettings = new JpaStorageSettings(); + } + + @Test + public void testExpunge_onSpecificTenant_willPerformExpungeOnSpecificTenant(){ + // given + when(myIResourceExpungeService.findHistoricalVersionsOfDeletedResources(any(), any(), anyInt())).thenReturn(List.of(JpaPid.fromId(1l))); + when(myIResourceExpungeService.findHistoricalVersionsOfNonDeletedResources(any(), any(), anyInt())).thenReturn(List.of(JpaPid.fromId(1l))); + myStorageSettings.setExpungeBatchSize(5); + + RequestDetails requestDetails = getRequestDetails(); + ExpungeOptions expungeOptions = new ExpungeOptions().setExpungeDeletedResources(true).setExpungeOldVersions(true); + + ExpungeOperation expungeOperation = new ExpungeOperation("Patient", null, expungeOptions, requestDetails); + + expungeOperation.setHapiTransactionServiceForTesting(myHapiTransactionService); + expungeOperation.setStorageSettingsForTesting(myStorageSettings); + expungeOperation.setExpungeDaoServiceForTesting(myIResourceExpungeService); + + expungeOperation.call(); + + // then + assertTransactionServiceWasInvokedWithTenantId(ourExpectedTenantId); + } + + private void assertTransactionServiceWasInvokedWithTenantId(String theExpectedTenantId) { + // we have set the expungeOptions to setExpungeDeletedResources and SetExpungeOldVersions to true. + // as a result, we will be making 5 trips to the db. let's make sure that each trip was done with + // the hapiTransaction service and that the tenantId was specified. + verify(myHapiTransactionService, times(5)).doExecute(myBuilderArgumentCaptor.capture(), any()); + List methodArgumentExecutionBuilders = myBuilderArgumentCaptor.getAllValues(); + + boolean allMatching = methodArgumentExecutionBuilders.stream() + .map(HapiTransactionService.ExecutionBuilder::getRequestDetailsForTesting) + .map(RequestDetails::getTenantId) + .allMatch(theExpectedTenantId::equals); + + assertThat(allMatching, is(equalTo(true))); + } + + private RequestDetails getRequestDetails() { + RequestDetails requestDetails = new ServletRequestDetails(); + requestDetails.setTenantId(ourExpectedTenantId); + return requestDetails; + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java new file mode 100644 index 00000000000..b9352eceea9 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java @@ -0,0 +1,67 @@ +package ca.uhn.fhir.jpa.dao.tx; + +import ca.uhn.fhir.jpa.dao.expunge.PartitionAwareSupplier; +import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class PartitionAwareSupplierTest { + + @Spy + private MockHapiTransactionService myHapiTransactionService; + + @Captor + private ArgumentCaptor myBuilderArgumentCaptor; + + private static final String ourExpectedTenantId = "TenantA"; + + @Test + public void testMethodFindInPartitionedContext_withRequestDetailsHavingTenantId_willExecuteOnSpecifiedPartition(){ + RequestDetails requestDetails = getRequestDetails(); + + PartitionAwareSupplier partitionAwareSupplier = new PartitionAwareSupplier(myHapiTransactionService, requestDetails); + partitionAwareSupplier.supplyInPartitionedContext(getResourcePersistentIdSupplier()); + + assertTransactionServiceWasInvokedWithTenantId(ourExpectedTenantId); + + } + + private Supplier> getResourcePersistentIdSupplier(){ + return () -> Collections.emptyList(); + } + + private void assertTransactionServiceWasInvokedWithTenantId(String theExpectedTenantId) { + verify(myHapiTransactionService, times(1)).doExecute(myBuilderArgumentCaptor.capture(), any()); + HapiTransactionService.ExecutionBuilder methodArgumentExecutionBuilder = myBuilderArgumentCaptor.getValue(); + + String requestDetailsTenantId = methodArgumentExecutionBuilder.getRequestDetailsForTesting().getTenantId(); + + assertThat(requestDetailsTenantId, is(equalTo(theExpectedTenantId))); + } + + private RequestDetails getRequestDetails() { + RequestDetails requestDetails = new ServletRequestDetails(); + requestDetails.setTenantId(ourExpectedTenantId); + return requestDetails; + } + +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java index 29c2eae81ea..8f7a5b86aad 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java @@ -40,7 +40,7 @@ public class MockHapiTransactionService extends HapiTransactionService { @Nullable @Override - protected T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { + public T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { return theCallback.doInTransaction(myTransactionStatus); } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java index 0a8ceb54240..9af4034046f 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +44,7 @@ public class ExpungeOperation implements Callable { public static final String THREAD_PREFIX = "expunge"; @Autowired - private IResourceExpungeService myExpungeDaoService; + private IResourceExpungeService myResourceExpungeService; @Autowired private JpaStorageSettings myStorageSettings; @@ -101,17 +102,14 @@ public class ExpungeOperation implements Callable { } private List findHistoricalVersionsOfDeletedResources() { - List retVal = myExpungeDaoService.findHistoricalVersionsOfDeletedResources( - myResourceName, myResourceId, myRemainingCount.get()); + List retVal = getPartitionAwareSupplier() + .supplyInPartitionedContext(() -> myResourceExpungeService.findHistoricalVersionsOfDeletedResources( + myResourceName, myResourceId, myRemainingCount.get())); + ourLog.debug("Found {} historical versions", retVal.size()); return retVal; } - private List findHistoricalVersionsOfNonDeletedResources() { - return myExpungeDaoService.findHistoricalVersionsOfNonDeletedResources( - myResourceName, myResourceId, myRemainingCount.get()); - } - private boolean expungeLimitReached() { boolean expungeLimitReached = myRemainingCount.get() <= 0; if (expungeLimitReached) { @@ -121,15 +119,21 @@ public class ExpungeOperation implements Callable { } private void expungeOldVersions() { - List historicalIds = findHistoricalVersionsOfNonDeletedResources(); + List historicalIds = getPartitionAwareSupplier() + .supplyInPartitionedContext(() -> myResourceExpungeService.findHistoricalVersionsOfNonDeletedResources( + myResourceName, myResourceId, myRemainingCount.get())); getPartitionRunner() .runInPartitionedThreads( historicalIds, - partition -> myExpungeDaoService.expungeHistoricalVersions( + partition -> myResourceExpungeService.expungeHistoricalVersions( myRequestDetails, partition, myRemainingCount)); } + private PartitionAwareSupplier getPartitionAwareSupplier() { + return new PartitionAwareSupplier(myTxService, myRequestDetails); + } + private PartitionRunner getPartitionRunner() { return new PartitionRunner( PROCESS_NAME, @@ -144,7 +148,7 @@ public class ExpungeOperation implements Callable { getPartitionRunner() .runInPartitionedThreads( theResourceIds, - partition -> myExpungeDaoService.expungeCurrentVersionOfResources( + partition -> myResourceExpungeService.expungeCurrentVersionOfResources( myRequestDetails, partition, myRemainingCount)); } @@ -152,11 +156,26 @@ public class ExpungeOperation implements Callable { getPartitionRunner() .runInPartitionedThreads( theResourceIds, - partition -> myExpungeDaoService.expungeHistoricalVersionsOfIds( + partition -> myResourceExpungeService.expungeHistoricalVersionsOfIds( myRequestDetails, partition, myRemainingCount)); } private ExpungeOutcome expungeOutcome() { return new ExpungeOutcome().setDeletedCount(myExpungeOptions.getLimit() - myRemainingCount.get()); } + + @VisibleForTesting + public void setHapiTransactionServiceForTesting(HapiTransactionService theHapiTransactionService) { + myTxService = theHapiTransactionService; + } + + @VisibleForTesting + public void setStorageSettingsForTesting(JpaStorageSettings theStorageSettings) { + myStorageSettings = theStorageSettings; + } + + @VisibleForTesting + public void setExpungeDaoServiceForTesting(IResourceExpungeService theIResourceExpungeService) { + myResourceExpungeService = theIResourceExpungeService; + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java new file mode 100644 index 00000000000..156690acc0f --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java @@ -0,0 +1,27 @@ +package ca.uhn.fhir.jpa.dao.expunge; + +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; + +import java.util.function.Supplier; +import javax.validation.constraints.NotNull; + +/** + * Utility class wrapping a supplier in a transaction with the purpose of performing the supply operation with a + * partitioned aware context. + */ +public class PartitionAwareSupplier { + private final HapiTransactionService myTransactionService; + private final RequestDetails myRequestDetails; + + @NotNull + public PartitionAwareSupplier(HapiTransactionService theTxService, RequestDetails theRequestDetails) { + myTransactionService = theTxService; + myRequestDetails = theRequestDetails; + } + + @NotNull + public T supplyInPartitionedContext(Supplier theResourcePersistentIdSupplier) { + return myTransactionService.withRequest(myRequestDetails).execute(tx -> theResourcePersistentIdSupplier.get()); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index a54a370a982..50d4669be68 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -483,6 +483,11 @@ public class HapiTransactionService implements IHapiTransactionService { public RequestPartitionId getRequestPartitionIdForTesting() { return myRequestPartitionId; } + + @VisibleForTesting + public RequestDetails getRequestDetailsForTesting() { + return myRequestDetails; + } } /** From 12519bde9f128a02f770ba6dbbac2acc388c96a2 Mon Sep 17 00:00:00 2001 From: StevenXLi Date: Fri, 6 Oct 2023 11:54:26 -0400 Subject: [PATCH 09/86] 5353 iterate on revincludes and includes does not return correct resources when used with non iterate revincludes (#5354) * added failing test * implemented solution * added doc generation * added changelog * fixed the order of includes and revincludes, based on what was implemented before * fixed formatting * fixed wording --------- Co-authored-by: Steven Li --- ...hen-used-with-non-iterate-revincludes.yaml | 6 + .../data/IMdmLinkJpaMetricsRepository.java | 19 +++ .../fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java | 19 +++ .../java/ca/uhn/fhir/jpa/entity/Search.java | 22 ++- .../search/PersistedJpaBundleProvider.java | 143 ++++++++---------- .../r4/ResourceProviderRevIncludeTest.java | 44 +++++- .../uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java | 19 +++ .../models/GenerateMetricsTestParameters.java | 19 +++ .../mdm/models/LinkMetricTestParameters.java | 19 +++ .../mdm/models/LinkScoreMetricTestParams.java | 19 +++ .../mdm/models/ResourceMetricTestParams.java | 19 +++ .../ca/uhn/fhir/jpa/mdm/package-info.java | 19 +++ .../jpa/mdm/util/MdmMetricSvcTestUtil.java | 19 +++ .../ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java | 19 +++ .../ca/uhn/fhir/mdm/api/IMdmMetricSvc.java | 19 +++ .../uhn/fhir/mdm/api/IMdmResourceDaoSvc.java | 19 +++ .../GenerateMdmLinkMetricParameters.java | 19 +++ .../params/GenerateMdmMetricsParameters.java | 19 +++ .../GenerateMdmResourceMetricsParameters.java | 19 +++ .../GenerateScoreMetricsParameters.java | 19 +++ .../ca/uhn/fhir/mdm/model/MdmLinkMetrics.java | 19 +++ .../fhir/mdm/model/MdmLinkScoreMetrics.java | 19 +++ .../ca/uhn/fhir/mdm/model/MdmMetrics.java | 19 +++ .../fhir/mdm/model/MdmResourceMetrics.java | 19 +++ .../mdm/util/MdmSearchParamBuildingUtils.java | 19 +++ .../provider/BulkDataExportProvider.java | 19 +++ 26 files changed, 550 insertions(+), 83 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml new file mode 100644 index 00000000000..fe14f3cd2b8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5353 +jira: SMILE-7451 +title: "Previously, when using revincludes and includes with iterate, while also using revincludes without iterate, +the result omitted some resources that should have been included. This issue has now been fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java index 03566ca238a..95d8e9bff82 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 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.entity.MdmLink; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java index 6f1b4a57ff4..db20645a928 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 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.mdm; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java index 2b17f21de35..7da0b2e2248 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java @@ -408,16 +408,26 @@ public class Search implements ICachedSearchDetails, Serializable { myLastUpdatedHigh = theUpperBound; } - private Set toIncList(boolean theWantReverse) { + private Set toIncList(boolean theWantReverse, boolean theIncludeAll, boolean theWantIterate) { HashSet retVal = new HashSet<>(); for (SearchInclude next : getIncludes()) { if (theWantReverse == next.isReverse()) { - retVal.add(new Include(next.getInclude(), next.isRecurse())); + if (theIncludeAll) { + retVal.add(new Include(next.getInclude(), next.isRecurse())); + } else { + if (theWantIterate == next.isRecurse()) { + retVal.add(new Include(next.getInclude(), next.isRecurse())); + } + } } } return Collections.unmodifiableSet(retVal); } + private Set toIncList(boolean theWantReverse) { + return toIncList(theWantReverse, true, true); + } + public Set toIncludesList() { return toIncList(false); } @@ -426,6 +436,14 @@ public class Search implements ICachedSearchDetails, Serializable { return toIncList(true); } + public Set toIncludesList(boolean iterate) { + return toIncList(false, false, iterate); + } + + public Set toRevIncludesList(boolean iterate) { + return toIncList(true, false, iterate); + } + public void addInclude(SearchInclude theInclude) { getIncludes().add(theInclude); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index 1738b02f120..a02d48baf20 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -46,7 +46,6 @@ import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.QueryParameterUtils; -import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails; @@ -463,72 +462,70 @@ public class PersistedJpaBundleProvider implements IBundleProvider { if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) { Integer maxIncludes = myStorageSettings.getMaximumIncludesToLoadPerPage(); - // Decide whether to perform include or revincludes first based on which one has iterate. - boolean performIncludesBeforeRevincludes = shouldPerformIncludesBeforeRevincudes(); - - if (performIncludesBeforeRevincludes) { - // Load _includes - Set includedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toIncludesList(), - false, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - if (maxIncludes != null) { - maxIncludes -= includedPids.size(); - } - thePids.addAll(includedPids); - includedPidList.addAll(includedPids); - - // Load _revincludes - Set revIncludedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toRevIncludesList(), - true, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - thePids.addAll(revIncludedPids); - includedPidList.addAll(revIncludedPids); - } else { - // Load _revincludes - Set revIncludedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toRevIncludesList(), - true, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - if (maxIncludes != null) { - maxIncludes -= revIncludedPids.size(); - } - thePids.addAll(revIncludedPids); - includedPidList.addAll(revIncludedPids); - - // Load _includes - Set includedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toIncludesList(), - false, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - thePids.addAll(includedPids); - includedPidList.addAll(includedPids); + // Load non-iterate _revincludes + Set nonIterateRevIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toRevIncludesList(false), + true, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= nonIterateRevIncludedPids.size(); } + thePids.addAll(nonIterateRevIncludedPids); + includedPidList.addAll(nonIterateRevIncludedPids); + + // Load non-iterate _includes + Set nonIterateIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toIncludesList(false), + false, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= nonIterateIncludedPids.size(); + } + thePids.addAll(nonIterateIncludedPids); + includedPidList.addAll(nonIterateIncludedPids); + + // Load iterate _revinclude + Set iterateRevIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toRevIncludesList(true), + true, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= iterateRevIncludedPids.size(); + } + thePids.addAll(iterateRevIncludedPids); + includedPidList.addAll(iterateRevIncludedPids); + + // Load iterate _includes + Set iterateIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toIncludesList(true), + false, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + thePids.addAll(iterateIncludedPids); + includedPidList.addAll(iterateIncludedPids); } // Execute the query and make sure we return distinct results @@ -547,20 +544,6 @@ public class PersistedJpaBundleProvider implements IBundleProvider { return resources; } - private boolean shouldPerformIncludesBeforeRevincudes() { - // When revincludes contain a :iterate, we should perform them last so they can iterate through the includes - // found so far - boolean retval = false; - - for (Include nextInclude : mySearchEntity.toRevIncludesList()) { - if (nextInclude.isRecurse()) { - retval = true; - break; - } - } - return retval; - } - public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) { myInterceptorBroadcaster = theInterceptorBroadcaster; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java index eb42c570e31..64d36445856 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java @@ -12,11 +12,15 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CareTeam; import org.hl7.fhir.r4.model.DetectedIssue; +import org.hl7.fhir.r4.model.Encounter; +import org.hl7.fhir.r4.model.EpisodeOfCare; import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.PractitionerRole; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,7 +144,7 @@ public class ResourceProviderRevIncludeTest extends BaseResourceProviderR4Test { //Ensure that the revincludes are included in the query list of the sql trace. //TODO GGG/KHS reduce this to something less than 6 by smarter iterating and getting the resource types earlier when needed. - assertEquals(6, sqlCapturingInterceptor.getQueryList().size()); + assertEquals(5, sqlCapturingInterceptor.getQueryList().size()); myInterceptorRegistry.unregisterInterceptor(sqlCapturingInterceptor); } @@ -174,4 +178,42 @@ public class ResourceProviderRevIncludeTest extends BaseResourceProviderR4Test { assertEquals(practitionerRoleId.getIdPart(), foundResources.get(2).getIdElement().getIdPart()); } + @Test + public void includeRevIncludeIterate() { + Patient p = new Patient(); + String methodName = "includeRevIncludeIterate"; + p.addName().setFamily(methodName); + IIdType pid = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); + + EpisodeOfCare episodeOfCare = new EpisodeOfCare(); + episodeOfCare.addIdentifier(new Identifier().setSystem("system1").setValue("value1")); + IIdType episodeOfCareId = myClient.create().resource(episodeOfCare).execute().getId().toUnqualifiedVersionless(); + + Encounter encounter = new Encounter(); + encounter.setSubject(new Reference(pid)); + encounter.addEpisodeOfCare(new Reference(episodeOfCareId)); + IIdType encounterId = myClient.create().resource(encounter).execute().getId().toUnqualifiedVersionless(); + + Task task = new Task(); + task.setEncounter(new Reference(encounterId)); + IIdType taskId = myClient.create().resource(task).execute().getId().toUnqualifiedVersionless(); + + // EpisodeOfCare?identifier=system1|value1&_revinclude=Encounter:episode-of-care&_include:iterate=Encounter:patient&_revinclude:iterate=Task:encounter + Bundle bundle = myClient.search() + .forResource(EpisodeOfCare.class) + .where(EpisodeOfCare.IDENTIFIER.exactly().systemAndIdentifier("system1", "value1")) + .revInclude(new Include("Encounter:episode-of-care")) + .include(new Include("Encounter:patient").setRecurse(true)) + .revInclude(new Include("Task:encounter").setRecurse(true)) + .returnBundle(Bundle.class) + .execute(); + + List foundResources = BundleUtil.toListOfResources(myFhirContext, bundle); + assertEquals(4, foundResources.size()); + assertEquals(episodeOfCareId.getIdPart(), foundResources.get(0).getIdElement().getIdPart()); + assertEquals(encounterId.getIdPart(), foundResources.get(1).getIdElement().getIdPart()); + assertEquals(taskId.getIdPart(), foundResources.get(2).getIdElement().getIdPart()); + assertEquals(pid.getIdPart(), foundResources.get(3).getIdElement().getIdPart()); + } + } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java index 18b51d0bc28..dd19eccec34 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm; import ca.uhn.fhir.jpa.mdm.models.GenerateMetricsTestParameters; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java index 8afeb438902..3e1a3a7d0f5 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm.models; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java index 95ba075b645..cc02c9cecef 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm.models; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java index 0434ccb69fb..4f44267c87f 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm.models; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java index 4ed1b52972c..5d61242e70c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm.models; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java index 13da8304544..5887575653c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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% + */ /** * This package is for persistence-agnostic mdm tests. * Even though the package is "jpaserver-test-utils", these diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java index 9e5a7503dcd..027ee7282c1 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.mdm.util; import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java index a3a9f75f56d..cd80f84c29a 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java index 451239bd243..aaea222e2cc 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api; import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java index 7afe2aa388e..866dd0b87dc 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api; import ca.uhn.fhir.interceptor.model.RequestPartitionId; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java index b8f0c2bfcbc..6a0ba65b3b8 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api.params; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java index 98dd96b8366..6af297b7786 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api.params; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java index 504f169604e..550227100d4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api.params; public class GenerateMdmResourceMetricsParameters { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java index 412703585de..19b94db4218 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.api.params; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java index 9d30b033030..35c63a36b4b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.model; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java index d5ea731ddea..423d7bc3cfb 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.model; import java.util.LinkedHashMap; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java index aed9fbe9f76..80316efd82b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.model; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java index 49939a5fd27..6d35c28b010 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.model; public class MdmResourceMetrics { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java index d2085329296..a33bb91e077 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 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.mdm.util; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java index 0aa762af2a0..7b3814b510d 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/rest/server/provider/BulkDataExportProvider.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR Test Utilities + * %% + * Copyright (C) 2014 - 2023 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 ca.uhn.fhir.rest.annotation.IdParam; From 02f25a9063d644d81a76b4f495a512b74c6edd41 Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:30:44 -0400 Subject: [PATCH 10/86] typo fix (#5358) --- .../ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md index 4fcecc9dc4e..eec3d90a613 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md @@ -816,13 +816,13 @@ Content-Type: application/fhir+json; charset=UTF-8 { "name":"resource", "resource": { - "resourceType":"Orgaization", + "resourceType":"Organization", "name": "McMaster Family Practice" } }, { "name":"resourceType", - "valueString": "Orgaization" + "valueString": "Organization" } ] } From 424f26b89747c407dbab54356b078a1aabcca04c Mon Sep 17 00:00:00 2001 From: c-schuler Date: Tue, 10 Oct 2023 09:13:21 -0600 Subject: [PATCH 11/86] Removed helperSvc bean override from JpaConfig (#5350) * Removed helperSvc bean override from JpaConfig * Added change log entry --- .../7_0_0/5349-jpa-config-bean-override-exception.yaml | 4 ++++ .../src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml new file mode 100644 index 00000000000..1f2504c4602 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5349 +title: "Removed duplicate helperSvc bean in JpaConfig (also defined in imported MdmJpaConfig) to resolve BeanDefinitionOverrideException" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 2fc257245cd..3a44a5f8ed2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -32,9 +32,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; -import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.api.svc.IMdmClearHelperSvc; import ca.uhn.fhir.jpa.api.svc.ISearchUrlJobMaintenanceSvc; import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; @@ -43,7 +41,6 @@ import ca.uhn.fhir.jpa.bulk.export.svc.BulkDataExportJobSchedulingHelperImpl; import ca.uhn.fhir.jpa.bulk.export.svc.BulkExportHelperService; import ca.uhn.fhir.jpa.bulk.imprt.api.IBulkDataImportSvc; import ca.uhn.fhir.jpa.bulk.imprt.svc.BulkDataImportSvcImpl; -import ca.uhn.fhir.jpa.bulk.mdm.MdmClearHelperSvcImpl; import ca.uhn.fhir.jpa.cache.IResourceVersionSvc; import ca.uhn.fhir.jpa.cache.ResourceVersionSvcDaoImpl; import ca.uhn.fhir.jpa.dao.DaoSearchParamProvider; @@ -872,11 +869,6 @@ public class JpaConfig { return new SearchUrlJobMaintenanceSvcImpl(theResourceSearchUrlSvc); } - @Bean - public IMdmClearHelperSvc helperSvc(IDeleteExpungeSvc theDeleteExpungeSvc) { - return new MdmClearHelperSvcImpl(theDeleteExpungeSvc); - } - @Bean public IResourceModifiedMessagePersistenceSvc subscriptionMessagePersistence( FhirContext theFhirContext, From 88e97800041fa0f9873c7272c7375c632d8cbc3c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 10 Oct 2023 20:36:33 -0400 Subject: [PATCH 12/86] Add additional resource docs to generated OpenAPI documentation (#5359) * Add additional resource docs to generated OpenAPI documentation * Test fix --- .../5355-add-openapi-resource-details.yaml | 8 +++ .../fhir/rest/openapi/OpenApiInterceptor.java | 42 +++++++++++++- .../rest/openapi/OpenApiInterceptorTest.java | 55 +++++++++++++++++++ .../fhir/rest/server/util/NarrativeUtil.java | 24 ++++---- .../rest/server/util/NarrativeUtilTest.java | 26 ++++----- .../java/ca/uhn/fhir/to/BaseController.java | 2 +- 6 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml new file mode 100644 index 00000000000..3e784c3b80f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml @@ -0,0 +1,8 @@ +--- +type: add +issue: 5355 +title: "The generated OpenAPI documentation produced by OpenApiInterceptor will now include + additional details in the individual resource type documentation, including the values of + *CapabilityStatement.rest.resource.documentation*, + *CapabilityStatement.rest.resource.profile*, and + *CapabilityStatement.rest.resource.supportedProfile*." diff --git a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java index 783e082a795..b49b65f39bd 100644 --- a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java +++ b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java @@ -64,6 +64,7 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; @@ -108,10 +109,12 @@ import java.util.Properties; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import static ca.uhn.fhir.rest.server.util.NarrativeUtil.sanitizeHtmlFragment; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -356,7 +359,7 @@ public class OpenApiInterceptor { String copyright = cs.getCopyright(); if (isNotBlank(copyright)) { - copyright = myFlexmarkRenderer.render(myFlexmarkParser.parse(copyright)); + copyright = renderMarkdown(copyright); context.setVariable("COPYRIGHT_HTML", copyright); } @@ -411,6 +414,11 @@ public class OpenApiInterceptor { theResponse.getWriter().close(); } + @Nonnull + private String renderMarkdown(String copyright) { + return myFlexmarkRenderer.render(myFlexmarkParser.parse(copyright)); + } + protected void populateOIDCVariables(ServletRequestDetails theRequestDetails, WebContext theContext) { theContext.setVariable("OAUTH2_REDIRECT_URL_PROPERTY", ""); } @@ -515,7 +523,7 @@ public class OpenApiInterceptor { Tag resourceTag = new Tag(); resourceTag.setName(resourceType); - resourceTag.setDescription("The " + resourceType + " FHIR resource type"); + resourceTag.setDescription(createResourceDescription(nextResource)); openApi.addTagsItem(resourceTag); // Instance Read @@ -624,6 +632,36 @@ public class OpenApiInterceptor { return openApi; } + @Nonnull + protected String createResourceDescription( + CapabilityStatement.CapabilityStatementRestResourceComponent theResource) { + StringBuilder b = new StringBuilder(); + b.append("The ").append(theResource.getType()).append(" FHIR resource type"); + + String documentation = theResource.getDocumentation(); + if (isNotBlank(documentation)) { + b.append("
"); + b.append(sanitizeHtmlFragment(renderMarkdown(documentation))); + } + + if (isNotBlank(theResource.getProfile())) { + b.append("
"); + b.append("Base profile: "); + b.append(sanitizeHtmlFragment(theResource.getProfile())); + } + + for (CanonicalType next : theResource.getSupportedProfile()) { + String nextSupportedProfile = next.getValueAsString(); + if (isNotBlank(nextSupportedProfile)) { + b.append("
"); + b.append("Supported profile: "); + b.append(sanitizeHtmlFragment(nextSupportedProfile)); + } + } + + return b.toString(); + } + protected void addSearchOperation( final OpenAPI openApi, final Operation operation, diff --git a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java index 2e0b0ac5067..441a656b826 100644 --- a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java +++ b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.openapi; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.*; @@ -32,16 +33,19 @@ import org.apache.http.client.methods.HttpGet; import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r5.model.ActorDefinition; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.function.Consumer; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; @@ -83,6 +87,28 @@ public class OpenApiInterceptorTest { assertThat(buttonTexts.toString(), buttonTexts, Matchers.contains("All", "System Level Operations", "OperationDefinition 1", "Observation", "Patient")); } + + @Test + public void testResourceDocsCopied() throws IOException { + myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor("OperationDefinition")); + myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor()); + myServer.registerInterceptor(new CapabilityStatementEnhancingInterceptor(cs->{ + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = findPatientResource(cs); + patientResource.setProfile("http://baseProfile"); + patientResource.addSupportedProfile("http://foo"); + patientResource.addSupportedProfile("http://bar"); + patientResource.setDocumentation("This is **bolded** documentation"); + })); + + org.hl7.fhir.r4.model.CapabilityStatement cs = myServer.getFhirClient().capabilities().ofType(org.hl7.fhir.r4.model.CapabilityStatement.class).execute(); + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = findPatientResource(cs); + assertEquals("This is **bolded** documentation", patientResource.getDocumentation()); + + String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/"; + String resp = fetchSwaggerUi(url); + } + + } @Nested @@ -111,6 +137,35 @@ public class OpenApiInterceptorTest { } } + @Interceptor + private static class CapabilityStatementEnhancingInterceptor { + + private final Consumer myConsumer; + + public CapabilityStatementEnhancingInterceptor(Consumer theConsumer) { + myConsumer = theConsumer; + } + + @Hook(Pointcut.SERVER_CAPABILITY_STATEMENT_GENERATED) + public void massageCapabilityStatement(IBaseConformance theCs) { + myConsumer.accept((org.hl7.fhir.r4.model.CapabilityStatement) theCs); + } + + } + + @Nonnull + private static org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent findPatientResource(org.hl7.fhir.r4.model.CapabilityStatement theCs) { + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = theCs + .getRest() + .get(0) + .getResource() + .stream() + .filter(t -> "Patient".equals(t.getType())) + .findFirst() + .orElseThrow(); + return patientResource; + } + @SuppressWarnings("JUnitMalformedDeclaration") abstract static class BaseOpenApiInterceptorTest { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java index bb7b89c0516..eb7ab4d9bfb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java @@ -47,10 +47,16 @@ public class NarrativeUtil { *
  • All other elements and attributes are removed
  • * */ - public static String sanitize(String theHtml) { - XhtmlNode node = new XhtmlNode(); - node.setValueAsString(theHtml); - return sanitize(node).getValueAsString(); + public static String sanitizeHtmlFragment(String theHtml) { + PolicyFactory idPolicy = + new HtmlPolicyBuilder().allowAttributes("id").globally().toFactory(); + + PolicyFactory policy = Sanitizers.FORMATTING + .and(Sanitizers.BLOCKS) + .and(Sanitizers.TABLES) + .and(Sanitizers.STYLES) + .and(idPolicy); + return policy.sanitize(theHtml); } /** @@ -70,15 +76,7 @@ public class NarrativeUtil { public static XhtmlNode sanitize(XhtmlNode theNode) { String html = theNode.getValueAsString(); - PolicyFactory idPolicy = - new HtmlPolicyBuilder().allowAttributes("id").globally().toFactory(); - - PolicyFactory policy = Sanitizers.FORMATTING - .and(Sanitizers.BLOCKS) - .and(Sanitizers.TABLES) - .and(Sanitizers.STYLES) - .and(idPolicy); - String safeHTML = policy.sanitize(html); + String safeHTML = sanitizeHtmlFragment(html); XhtmlNode retVal = new XhtmlNode(); retVal.setValueAsString(safeHTML); diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java index 7278c25f8af..a8bc5d34a46 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java @@ -9,21 +9,21 @@ public class NarrativeUtilTest { @ParameterizedTest @CsvSource({ - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - " ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "hello ,
    hello
    ", - "empty , null", - "null , null" + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + " ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "hello , hello", + "empty , empty", + "null , empty" }) public void testValidateIsCaseInsensitive(String theHtml, String theExpected) { - String output = NarrativeUtil.sanitize(fixNull(theHtml)); - assertEquals(fixNull(theExpected), output); + String output = NarrativeUtil.sanitizeHtmlFragment(fixNull(theHtml)); + assertEquals(fixNull(theExpected), fixNull(output)); } private String fixNull(String theExpected) { diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java index 0b9d14f3162..49c2f4c4ea5 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/BaseController.java @@ -583,7 +583,7 @@ public class BaseController { theModelMap.put("resultBodyIsLong", resultBodyText.length() > 1000); theModelMap.put("requestHeaders", requestHeaders); theModelMap.put("responseHeaders", responseHeaders); - theModelMap.put("narrative", NarrativeUtil.sanitize(narrativeString)); + theModelMap.put("narrative", NarrativeUtil.sanitizeHtmlFragment(narrativeString)); theModelMap.put("latencyMs", theLatency); theModelMap.put("config", myConfig); From c832cf05078d3d002414aee6a3c7654ad7d0b6c8 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Wed, 11 Oct 2023 12:47:52 -0600 Subject: [PATCH 13/86] Hapi cr codecache (#5360) * WIP code cache config * WIP test * Passing unit test * cachelistener IT * hardening cache listener tests * cache listener fixes & tests, measureService migration, add reporter parameter to r4 * fix ELMCache listener to match on version, update test, remove reporter parameter * edit inline notes * changelog, version bump, spotless apply --------- Co-authored-by: Jonathan Percival --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- .../5356-cr-codecache-invalidation-bug.yaml | 4 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- .../CodeCacheResourceChangeListener.java | 23 +- .../fhir/cr/config/dstu3/CrDstu3Config.java | 4 +- .../ca/uhn/fhir/cr/config/r4/CrR4Config.java | 4 +- .../fhir/cr/dstu3/IMeasureServiceFactory.java | 4 +- .../fhir/cr/dstu3/measure/MeasureService.java | 158 - .../fhir/cr/r4/IMeasureServiceFactory.java | 4 +- .../r4/measure/MeasureOperationsProvider.java | 13 +- .../fhir/cr/r4/measure/MeasureService.java | 216 - .../java/ca/uhn/fhir/cr/TestCrConfig.java | 53 +- .../uhn/fhir/cr/dstu3/TestCrDstu3Config.java | 57 +- .../ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java | 6 + .../fhir/cr/r4/CrResourceListenerTests.java | 176 + .../cr/r4/R4MeasureOperationProviderIT.java | 8 +- .../ca/uhn/fhir/cr/r4/TestCrR4Config.java | 58 +- ...ColorectalCancerScreeningsFHIR-bundle.json | 2 +- .../multiversion/EXM130-0.0.001-bundle.json | 916 ++++ .../multiversion/EXM130-0.0.002-bundle.json | 920 ++++ .../multiversion/valueset-version-bundle.json | 3780 +++++++++++++++++ hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 96 files changed, 6063 insertions(+), 503 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml delete mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java delete mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java create mode 100644 hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java create mode 100644 hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json create mode 100644 hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json create mode 100644 hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 17dbf26162e..d0dee2ba288 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 150ce2c1295..8fca23316db 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 98e10ec3c10..74170e83cf3 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 346a929a547..a3a762e6942 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 4f4df91ba18..1c5f54d6dae 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-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 c378ea02796..cdf8bfd4252 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 04a840db292..4a2718254e6 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index a98004d7d99..e90ff04faef 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index b91cee3d080..54405842ff1 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index ab4e11fa4c1..7ccf7bcadc3 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index f736e14d80d..cfdfed5e12d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index a08d329cbee..e6503bfc300 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 2d137c54951..5e69ddf10f4 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml new file mode 100644 index 00000000000..b3a9c984458 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5356 +title: "Clinical reasoning bug that did not invalidate resources in repository api global caches for terminology and libraries when updates/deletes were made." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 89cc744fcc2..1a91d9252e9 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 17ae3e225c7..115ce1185e2 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 32ffc32391c..9323628d70b 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 954eac61b5f..8b5b0bb7239 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index bee277633ec..9ea06aa38fb 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index a2ac4d063a4..8a86baff174 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index db9e2677280..27cd43e8e83 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index b7e925ea214..d9b8c991f08 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 0eb9cabc642..1c107b413b0 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index a2728494bf4..95cb771b09a 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index ed3cab96c80..ff5c808ea66 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 - 6.9.9-SNAPSHOT + 6.9.10-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 2e00cf23d76..92d2352b9ae 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index f03dae46f3b..5cd55a517fa 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 9253e66d122..a854824c9a8 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 22ad4ef0907..83ef4b6dc18 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 1a9b4c1527a..50cd37ca724 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index c5a65a61dd0..1bc77fdb078 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index dbb6efb3f9c..214c9b20a14 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 266e0577cf8..976f53f4085 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index f01dc291872..a5633255950 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index c57d846a2d1..b901a4bb13c 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index a52010ad2d0..7054dfb2c21 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 7a91609e596..cfd1bc822de 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 - 6.9.9-SNAPSHOT + 6.9.10-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 a3b872bcbb6..0f6550cef86 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 31a240fdbb3..036155a7e47 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 - 6.9.9-SNAPSHOT + 6.9.10-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 119a3ced85c..2a25920f44e 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 4d50f71fa86..0a009891136 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.9-SNAPSHOT + 6.9.10-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 148c5717fb5..3d44dafc894 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 - 6.9.9-SNAPSHOT + 6.9.10-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 debccca5b4b..9e3c5a056bf 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 - 6.9.9-SNAPSHOT + 6.9.10-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 9b6ba0cdf17..091ec2fe405 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 - 6.9.9-SNAPSHOT + 6.9.10-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 05465b1fd32..aff764c6c2d 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 - 6.9.9-SNAPSHOT + 6.9.10-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 8d89550158d..e19bcae6932 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 - 6.9.9-SNAPSHOT + 6.9.10-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 6a845cc9ffc..5e4a7670ff7 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 16232047a4f..bea999f9baa 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index f7ee91e88a1..44cbfd4db96 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 53aaee618a2..0b710f29008 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 259a89b3dbb..f610cd6e8a2 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 791bfc09bbf..e878ac62fbf 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 6adae32a21c..780db0b63f8 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java index 8055ea1b1b4..4c7a8556c13 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.cache.IResourceChangeEvent; import ca.uhn.fhir.jpa.cache.IResourceChangeListener; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.cql.engine.runtime.Code; @@ -45,14 +44,13 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener org.slf4j.LoggerFactory.getLogger(CodeCacheResourceChangeListener.class); private final IFhirResourceDao myValueSetDao; - private final Map> myGlobalCodeCache; + private final Map> myGlobalValueSetCache; private final Function myUrlFunction; private final Function myVersionFunction; - public CodeCacheResourceChangeListener( - DaoRegistry theDaoRegistry, Map> theGlobalCodeCache) { + public CodeCacheResourceChangeListener(DaoRegistry theDaoRegistry, Map> theGlobalValueSetCache) { this.myValueSetDao = theDaoRegistry.getResourceDao("ValueSet"); - this.myGlobalCodeCache = theGlobalCodeCache; + this.myGlobalValueSetCache = theGlobalValueSetCache; this.myUrlFunction = Reflections.getUrlFunction(myValueSetDao.getResourceType()); this.myVersionFunction = Reflections.getVersionFunction(myValueSetDao.getResourceType()); } @@ -89,7 +87,7 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener IBaseResource valueSet; try { - valueSet = this.myValueSetDao.read(theId); + valueSet = this.myValueSetDao.read(theId.toUnqualifiedVersionless()); } // This happens when a Library is deleted entirely, so it's impossible to look up // name and version. @@ -97,13 +95,20 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener ourLog.debug( "Failed to locate resource {} to look up url and version. Clearing all codes from cache.", theId.getValueAsString()); - this.myGlobalCodeCache.clear(); + myGlobalValueSetCache.clear(); return; } String url = this.myUrlFunction.apply(valueSet); - String version = this.myVersionFunction.apply(valueSet); - this.myGlobalCodeCache.remove(new VersionedIdentifier().withId(url).withVersion(version)); + var valuesets = myGlobalValueSetCache.keySet(); + + for (String key : valuesets) { + var urlKey = key; + if (urlKey.contains(url)) { + myGlobalValueSetCache.remove(key); + ourLog.warn("Successfully removed valueSet from ValueSetCache: " + url + " due to updated resource"); + } + } } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java index 1a19d6c3214..ed8e17306ae 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java @@ -27,9 +27,9 @@ import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.cr.config.RepositoryConfig; import ca.uhn.fhir.cr.dstu3.IMeasureServiceFactory; import ca.uhn.fhir.cr.dstu3.measure.MeasureOperationsProvider; -import ca.uhn.fhir.cr.dstu3.measure.MeasureService; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; +import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -45,7 +45,7 @@ public class CrDstu3Config { @Bean IMeasureServiceFactory dstu3MeasureServiceFactory( IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theEvaluationOptions) { - return rd -> new MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); + return rd -> new Dstu3MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); } @Bean diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java index f4a053eb473..d77e3073047 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.cr.r4.ISubmitDataProcessorFactory; import ca.uhn.fhir.cr.r4.cqlexecution.CqlExecutionOperationProvider; import ca.uhn.fhir.cr.r4.measure.CareGapsOperationProvider; import ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider; -import ca.uhn.fhir.cr.r4.measure.MeasureService; import ca.uhn.fhir.cr.r4.measure.SubmitDataProvider; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; @@ -40,6 +39,7 @@ import org.opencds.cqf.fhir.cr.cql.r4.R4CqlExecutionService; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.cr.measure.r4.R4CareGapsService; +import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureService; import org.opencds.cqf.fhir.cr.measure.r4.R4SubmitDataService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; @@ -58,7 +58,7 @@ public class CrR4Config { @Bean IMeasureServiceFactory r4MeasureServiceFactory( IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theEvaluationOptions) { - return rd -> new MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); + return rd -> new R4MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); } @Bean diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java index 0f8951bce54..cbf6696adf2 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.cr.dstu3; -import ca.uhn.fhir.cr.dstu3.measure.MeasureService; import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureService; @FunctionalInterface public interface IMeasureServiceFactory { - MeasureService create(RequestDetails theRequestDetails); + Dstu3MeasureService create(RequestDetails theRequestDetails); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java deleted file mode 100644 index 24876ddc9f0..00000000000 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java +++ /dev/null @@ -1,158 +0,0 @@ -/*- - * #%L - * HAPI FHIR - Clinical Reasoning - * %% - * Copyright (C) 2014 - 2023 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.cr.dstu3.measure; - -import ca.uhn.fhir.util.BundleBuilder; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.CodeableConcept; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.ContactDetail; -import org.hl7.fhir.dstu3.model.ContactPoint; -import org.hl7.fhir.dstu3.model.Endpoint; -import org.hl7.fhir.dstu3.model.Enumerations; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.MeasureReport; -import org.hl7.fhir.dstu3.model.SearchParameter; -import org.hl7.fhir.dstu3.model.StringType; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; -import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureProcessor; - -import java.util.Collections; -import java.util.List; - -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.COUNTRY_CODING_SYSTEM_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_DISPLAY; - -public class MeasureService { - private final Repository myRepository; - private final MeasureEvaluationOptions myMeasureEvaluationOptions; - - public MeasureService(Repository theRepository, MeasureEvaluationOptions theMeasureEvaluationOptions) { - myRepository = theRepository; - myMeasureEvaluationOptions = theMeasureEvaluationOptions; - } - - public static final List CQI_CONTACT_DETAIL = Collections.singletonList(new ContactDetail() - .addTelecom(new ContactPoint() - .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("http://www.hl7.org/Special/committees/cqi/index.cfm"))); - - public static final List US_JURISDICTION_CODING = Collections.singletonList(new CodeableConcept() - .addCoding(new Coding(COUNTRY_CODING_SYSTEM_CODE, US_COUNTRY_CODE, US_COUNTRY_DISPLAY))); - - public static final SearchParameter SUPPLEMENTAL_DATA_SEARCHPARAMETER = (SearchParameter) new SearchParameter() - .setUrl(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL) - .setVersion(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION) - .setName("DEQMMeasureReportSupplementalData") - .setStatus(Enumerations.PublicationStatus.ACTIVE) - .setDate(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE) - .setPublisher("HL7 International - Clinical Quality Information Work Group") - .setContact(CQI_CONTACT_DETAIL) - .setDescription(String.format( - "Returns resources (supplemental data) from references on extensions on the MeasureReport with urls matching %s.", - MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setJurisdiction(US_JURISDICTION_CODING) - .addBase("MeasureReport") - .setCode("supplemental-data") - .setType(Enumerations.SearchParamType.REFERENCE) - .setExpression(String.format( - "MeasureReport.extension('%s').value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpath(String.format( - "f:MeasureReport/f:extension[@url='%s'].value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpathUsage(SearchParameter.XPathUsageType.NORMAL) - .setTitle("Supplemental Data") - .setId("deqm-measurereport-supplemental-data"); - - /** - * Get The details (such as tenant) of this request. Usually auto-populated HAPI. - * - */ - - /** - * Implements the $evaluate-measure - * operation found in the - * FHIR Clinical - * Reasoning Module. This implementation aims to be compatible with the CQF - * IG. - * - * @param theId the Id of the Measure to evaluate - * @param thePeriodStart The start of the reporting period - * @param thePeriodEnd The end of the reporting period - * @param theReportType The type of MeasureReport to generate - * @param thePractitioner the practitioner to use for the evaluation - * @param theLastReceivedOn the date the results of this measure were last - * received. - * @param theProductLine the productLine (e.g. Medicare, Medicaid, etc) to use - * for the evaluation. This is a non-standard parameter. - * @param theAdditionalData the data bundle containing additional data - * @param theTerminologyEndpoint the endpoint of terminology services for your measure valuesets - * @return the calculated MeasureReport - */ - public MeasureReport evaluateMeasure( - IdType theId, - String thePeriodStart, - String thePeriodEnd, - String theReportType, - String theSubject, - String thePractitioner, - String theLastReceivedOn, - String theProductLine, - Bundle theAdditionalData, - Endpoint theTerminologyEndpoint) { - - ensureSupplementalDataElementSearchParameter(); - - var dstu3MeasureProcessor = new Dstu3MeasureProcessor(myRepository, myMeasureEvaluationOptions); - - MeasureReport report = dstu3MeasureProcessor.evaluateMeasure( - theId, - thePeriodStart, - thePeriodEnd, - theReportType, - Collections.singletonList(theSubject), - theAdditionalData); - - if (theProductLine != null) { - Extension ext = new Extension(); - ext.setUrl("http://hl7.org/fhir/us/cqframework/cqfmeasures/StructureDefinition/cqfm-productLine"); - ext.setValue(new StringType(theProductLine)); - report.addExtension(ext); - } - - return report; - } - - protected void ensureSupplementalDataElementSearchParameter() { - // create a transaction bundle - BundleBuilder builder = new BundleBuilder(myRepository.fhirContext()); - - // set the request to be condition on code == supplemental data - builder.addTransactionCreateEntry(SUPPLEMENTAL_DATA_SEARCHPARAMETER).conditional("code=supplemental-data"); - myRepository.transaction(builder.getBundle()); - } -} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java index 374f82e26a1..e99ce0547ed 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.cr.r4; -import ca.uhn.fhir.cr.r4.measure.MeasureService; import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureService; @FunctionalInterface public interface IMeasureServiceFactory { - MeasureService create(RequestDetails theRequestDetails); + R4MeasureService create(RequestDetails theRequestDetails); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java index 772c0e491bd..5526dc8f1dd 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java @@ -32,6 +32,7 @@ import org.hl7.fhir.r4.model.Endpoint; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.MeasureReport; +import org.opencds.cqf.fhir.utility.monad.Eithers; import org.springframework.beans.factory.annotation.Autowired; public class MeasureOperationsProvider { @@ -77,16 +78,18 @@ public class MeasureOperationsProvider { throws InternalErrorException, FHIRException { return myR4MeasureServiceFactory .create(theRequestDetails) - .evaluateMeasure( - theId, + .evaluate( + Eithers.forMiddle3(theId), thePeriodStart, thePeriodEnd, theReportType, theSubject, - thePractitioner, theLastReceivedOn, - theProductLine, + null, + theTerminologyEndpoint, + null, theAdditionalData, - theTerminologyEndpoint); + theProductLine, + thePractitioner); } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java deleted file mode 100644 index 05eae83cccd..00000000000 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java +++ /dev/null @@ -1,216 +0,0 @@ -/*- - * #%L - * HAPI FHIR - Clinical Reasoning - * %% - * Copyright (C) 2014 - 2023 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.cr.r4.measure; - -import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.util.BundleBuilder; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ContactDetail; -import org.hl7.fhir.r4.model.ContactPoint; -import org.hl7.fhir.r4.model.Endpoint; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.MeasureReport; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.SearchParameter; -import org.hl7.fhir.r4.model.StringType; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; -import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureProcessor; -import org.opencds.cqf.fhir.cr.measure.r4.R4RepositorySubjectProvider; -import org.opencds.cqf.fhir.utility.iterable.BundleIterator; -import org.opencds.cqf.fhir.utility.monad.Eithers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.COUNTRY_CODING_SYSTEM_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_PRODUCT_LINE_EXT_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_DISPLAY; - -public class MeasureService { - - private final Repository myRepository; - private final MeasureEvaluationOptions myMeasureEvaluationOptions; - private Logger ourLogger = LoggerFactory.getLogger(MeasureService.class); - - public MeasureService(Repository theRepository, MeasureEvaluationOptions theMeasureEvaluationOptions) { - this.myRepository = theRepository; - this.myMeasureEvaluationOptions = theMeasureEvaluationOptions; - } - - public static final List CQI_CONTACTDETAIL = Collections.singletonList(new ContactDetail() - .addTelecom(new ContactPoint() - .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("http://www.hl7.org/Special/committees/cqi/index.cfm"))); - - public static final List US_JURISDICTION_CODING = Collections.singletonList(new CodeableConcept() - .addCoding(new Coding(COUNTRY_CODING_SYSTEM_CODE, US_COUNTRY_CODE, US_COUNTRY_DISPLAY))); - - public static final SearchParameter SUPPLEMENTAL_DATA_SEARCHPARAMETER = (SearchParameter) new SearchParameter() - .setUrl(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL) - .setVersion(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION) - .setName("DEQMMeasureReportSupplementalData") - .setStatus(Enumerations.PublicationStatus.ACTIVE) - .setDate(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE) - .setPublisher("HL7 International - Clinical Quality Information Work Group") - .setContact(CQI_CONTACTDETAIL) - .setDescription(String.format( - "Returns resources (supplemental data) from references on extensions on the MeasureReport with urls matching %s.", - MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setJurisdiction(US_JURISDICTION_CODING) - .addBase("MeasureReport") - .setCode("supplemental-data") - .setType(Enumerations.SearchParamType.REFERENCE) - .setExpression(String.format( - "MeasureReport.extension('%s').value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpath(String.format( - "f:MeasureReport/f:extension[@url='%s'].value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpathUsage(SearchParameter.XPathUsageType.NORMAL) - .setTitle("Supplemental Data") - .setId("deqm-measurereport-supplemental-data"); - - /** - * Implements the $evaluate-measure - * operation found in the - * FHIR Clinical - * Reasoning Module. This implementation aims to be compatible with the CQF - * IG. - * - * @param theId the Id of the Measure to evaluate - * @param thePeriodStart The start of the reporting period - * @param thePeriodEnd The end of the reporting period - * @param theReportType The type of MeasureReport to generate - * @param thePractitioner the thePractitioner to use for the evaluation - * @param theLastReceivedOn the date the results of this measure were last - * received. - * @param theProductLine the theProductLine (e.g. Medicare, Medicaid, etc) to use - * for the evaluation. This is a non-standard parameter. - * @param theAdditionalData the data bundle containing additional data - * @param theTerminologyEndpoint the endpoint of terminology services for your measure valuesets - * @return the calculated MeasureReport - */ - public MeasureReport evaluateMeasure( - IdType theId, - String thePeriodStart, - String thePeriodEnd, - String theReportType, - String theSubject, - String thePractitioner, - String theLastReceivedOn, - String theProductLine, - Bundle theAdditionalData, - Endpoint theTerminologyEndpoint) { - - ensureSupplementalDataElementSearchParameter(); - - var r4MeasureProcessor = - new R4MeasureProcessor(myRepository, myMeasureEvaluationOptions, new R4RepositorySubjectProvider()); - - MeasureReport measureReport = null; - - // SUBJECT LIST SETTERS - if (StringUtils.isBlank(theSubject) && StringUtils.isNotBlank(thePractitioner)) { - List subjectIds = getPractitionerPatients(thePractitioner, myRepository); - - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), - thePeriodStart, - thePeriodEnd, - theReportType, - subjectIds, - theAdditionalData); - - } else if (StringUtils.isNotBlank(theSubject)) { - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), - thePeriodStart, - thePeriodEnd, - theReportType, - Collections.singletonList(theSubject), - theAdditionalData); - - } else if (StringUtils.isBlank(theSubject) && StringUtils.isBlank(thePractitioner)) { - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), thePeriodStart, thePeriodEnd, theReportType, null, theAdditionalData); - } - // add ProductLine after report is generated - addProductLineExtension(measureReport, theProductLine); - - return measureReport; - } - - private List getPractitionerPatients(String thePractitioner, Repository theRepository) { - List patients = new ArrayList<>(); - - Map> map = new HashMap<>(); - map.put( - "general-practitioner", - Collections.singletonList(new ReferenceParam( - thePractitioner.startsWith("Practitioner/") - ? thePractitioner - : "Practitioner/" + thePractitioner))); - - var bundle = theRepository.search(Bundle.class, Patient.class, map); - var iterator = new BundleIterator<>(theRepository, bundle); - - while (iterator.hasNext()) { - var patient = iterator.next().getResource(); - var refString = patient.getIdElement().getResourceType() + "/" - + patient.getIdElement().getIdPart(); - patients.add(refString); - } - return patients; - } - - private void addProductLineExtension(MeasureReport theMeasureReport, String theProductLine) { - if (theProductLine != null) { - Extension ext = new Extension(); - ext.setUrl(MEASUREREPORT_PRODUCT_LINE_EXT_URL); - ext.setValue(new StringType(theProductLine)); - theMeasureReport.addExtension(ext); - } - } - - protected void ensureSupplementalDataElementSearchParameter() { - // create a transaction bundle - BundleBuilder builder = new BundleBuilder(myRepository.fhirContext()); - - // set the request to be condition on code == supplemental data - builder.addTransactionCreateEntry(SUPPLEMENTAL_DATA_SEARCHPARAMETER).conditional("code=supplemental-data"); - myRepository.transaction(builder.getBundle()); - } -} diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java index 3814bba357f..0d801b46332 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.cr; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener; @@ -8,7 +9,15 @@ import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.cache.IResourceChangeListener; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCacheRefresher; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCache; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor; +import ca.uhn.fhir.jpa.cache.ResourceVersionMap; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; @@ -16,6 +25,7 @@ import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; @@ -32,6 +42,9 @@ import org.cqframework.cql.cql2elm.model.Model; import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.runtime.Code; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -39,10 +52,14 @@ import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Scope; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + @Configuration @Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class}) @@ -91,7 +108,6 @@ public class TestCrConfig { } @Bean - @Scope("prototype") public ModelManager modelManager(Map theGlobalModelCache) { return new ModelManager(theGlobalModelCache); } @@ -107,34 +123,49 @@ public class TestCrConfig { } @Bean - public Map> globalCodeCache() { + public Map> globalValueSetCache() { return new ConcurrentHashMap<>(); } + @Bean - @Primary public ElmCacheResourceChangeListener elmCacheResourceChangeListener( IResourceChangeListenerRegistry theResourceChangeListenerRegistry, DaoRegistry theDaoRegistry, - Map theGlobalLibraryCache) { + EvaluationSettings theEvaluationSettings) { ElmCacheResourceChangeListener listener = - new ElmCacheResourceChangeListener(theDaoRegistry, theGlobalLibraryCache); + new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache()); theResourceChangeListenerRegistry.registerResourceResourceChangeListener( "Library", SearchParameterMap.newSynchronous(), listener, 1000); return listener; } @Bean - @Primary public CodeCacheResourceChangeListener codeCacheResourceChangeListener( IResourceChangeListenerRegistry theResourceChangeListenerRegistry, - DaoRegistry theDaoRegistry, - Map> theGlobalCodeCache) { - CodeCacheResourceChangeListener listener = - new CodeCacheResourceChangeListener(theDaoRegistry, theGlobalCodeCache); + EvaluationSettings theEvaluationSettings, + DaoRegistry theDaoRegistry) { + + CodeCacheResourceChangeListener listener = new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache()); + //registry theResourceChangeListenerRegistry.registerResourceResourceChangeListener( - "ValueSet", SearchParameterMap.newSynchronous(), listener, 1000); + "ValueSet", SearchParameterMap.newSynchronous(), listener,1000); + return listener; } + @Bean + public IResourceChangeListenerRegistry resourceChangeListenerRegistry(InMemoryResourceMatcher theInMemoryResourceMatcher, FhirContext theFhirContext, ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory) { + return new ResourceChangeListenerRegistryImpl(theFhirContext, theResourceChangeListenerCacheFactory, theInMemoryResourceMatcher); + } + + @Bean + IResourceChangeListenerCacheRefresher resourceChangeListenerCacheRefresher() { + return new ResourceChangeListenerCacheRefresherImpl(); + } + @Bean + public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() { + return new ResourceChangeListenerRegistryInterceptor(); + } + } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java index 2b6d5eb5025..0895237526d 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java @@ -14,6 +14,7 @@ import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.execution.CqlEngine; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.utility.ValidationProfile; @@ -23,6 +24,7 @@ import org.springframework.context.annotation.Import; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -43,9 +45,13 @@ public class TestCrDstu3Config { } @Bean - public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache) { + public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, + Map> theGlobalValueSetCache) { var evaluationSettings = EvaluationSettings.getDefault(); - var cqlEngineOptions = evaluationSettings.getEngineOptions(); + var cqlOptions = evaluationSettings.getCqlOptions(); + + var cqlEngineOptions = cqlOptions.getCqlEngineOptions(); Set options = EnumSet.noneOf(CqlEngine.Options.class); if (theCqlProperties.isCqlRuntimeEnableExpressionCaching()) { options.add(CqlEngine.Options.EnableExpressionCaching); @@ -54,18 +60,61 @@ public class TestCrDstu3Config { options.add(CqlEngine.Options.EnableValidation); } cqlEngineOptions.setOptions(options); - var cqlOptions = evaluationSettings.getCqlOptions(); cqlOptions.setCqlEngineOptions(cqlEngineOptions); var cqlCompilerOptions = new CqlCompilerOptions(); - cqlCompilerOptions.setCompatibilityLevel("1.3"); + if (theCqlProperties.isEnableDateRangeOptimization()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization); + } + if (theCqlProperties.isEnableAnnotations()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations); + } + if (theCqlProperties.isEnableLocators()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators); + } + if (theCqlProperties.isEnableResultsType()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes); + } + cqlCompilerOptions.setVerifyOnly(theCqlProperties.isCqlCompilerVerifyOnly()); + if (theCqlProperties.isEnableDetailedErrors()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors); + } + cqlCompilerOptions.setErrorLevel(theCqlProperties.getCqlCompilerErrorSeverityLevel()); + if (theCqlProperties.isDisableListTraversal()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal); + } + if (theCqlProperties.isDisableListDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion); + } + if (theCqlProperties.isDisableListPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion); + } + if (theCqlProperties.isEnableIntervalDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion); + } + if (theCqlProperties.isEnableIntervalPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion); + } + if (theCqlProperties.isDisableMethodInvocation()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation); + } + if (theCqlProperties.isRequireFromKeyword()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword); + } + cqlCompilerOptions.setValidateUnits(theCqlProperties.isCqlCompilerValidateUnits()); + if (theCqlProperties.isDisableDefaultModelInfoLoad()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad); + } + cqlCompilerOptions.setSignatureLevel(theCqlProperties.getCqlCompilerSignatureLevel()); + cqlCompilerOptions.setCompatibilityLevel(theCqlProperties.getCqlCompilerCompatibilityLevel()); cqlCompilerOptions.setAnalyzeDataRequirements(theCqlProperties.isCqlCompilerAnalyzeDataRequirements()); cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); + evaluationSettings.setValueSetCache(theGlobalValueSetCache); return evaluationSettings; } } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java index 1d0eeafeeb5..1495d4ad4b5 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java @@ -8,6 +8,9 @@ import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCache; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.parser.IParser; @@ -35,7 +38,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.context.ContextConfiguration; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; @@ -70,6 +75,7 @@ public abstract class BaseCrR4TestServer extends BaseJpaR4Test implements IResou ourClient.unregisterInterceptor(mySimpleHeaderInterceptor); myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields()); myEvaluationSettings.getLibraryCache().clear(); + myEvaluationSettings.getValueSetCache().clear(); } @Autowired RestfulServer ourRestfulServer; diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java new file mode 100644 index 00000000000..b10071ce244 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java @@ -0,0 +1,176 @@ +package ca.uhn.fhir.cr.r4; + +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.MeasureReport; +import org.hl7.fhir.r4.model.Parameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(SpringExtension.class) +public class CrResourceListenerTests extends BaseCrR4TestServer { + @Autowired + EvaluationSettings myEvaluationSettings; + @Autowired + ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry; + @Autowired + ResourceChangeListenerCacheRefresherImpl myResourceChangeListenerCacheRefresher; + + + public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){ + + var parametersEval = new Parameters(); + parametersEval.addParameter("periodStart", new DateType(periodStart)); + parametersEval.addParameter("periodEnd", new DateType(periodEnd)); + parametersEval.addParameter("practitioner", practitioner); + parametersEval.addParameter("reportType", reportType); + parametersEval.addParameter("subject", subject); + + var report = ourClient.operation().onInstance("Measure/" + measureId) + .named("$evaluate-measure") + .withParameters(parametersEval) + .returnResourceType(MeasureReport.class) + .execute(); + + assertNotNull(report); + + return report; + } + + @Test + void testCodeCacheInvalidation() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("ValueSet")); + + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + // This is a manual init + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cached valueSets + assertEquals(11, myEvaluationSettings.getValueSetCache().size()); + + //remove valueset from server + var id = new IdType("ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001"); + ourClient.delete().resourceById(id).execute(); + + // This is a manual refresh + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //_ALL_ valuesets should be removed from cache + assertEquals(0, myEvaluationSettings.getValueSetCache().size()); + } + + @Test + void testElmCacheInvalidation() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + // This is a manual init + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cached libraries + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + //remove Library from server + var id = new IdType("Library/ColorectalCancerScreeningsFHIR"); + ourClient.delete().resourceById(id).execute(); + + // This is a manual refresh + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //_ALL_ Libraries should be removed from cache + assertEquals(0, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testAddNewVersionOfSameLibrary() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached libraries from bundle + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add same version of measure Library to server with minor edits + loadBundle("multiversion/EXM130-0.0.001-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should be invalidated for matching library name and version + assertEquals(6, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testNewVersionLibraryAdd() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached libraries from bundle + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add same version of measure Library to server with minor edits + loadBundle("multiversion/EXM130-0.0.002-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should not be invalidated because name and version don't have a match in cache + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testNewVersionValueSetAdd() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("ValueSet")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds valueset to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached valueset from bundle + assertEquals(11, myEvaluationSettings.getValueSetCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add new version of valueset to server + loadBundle("multiversion/valueset-version-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should be invalidated for valueset url and removed + assertEquals(10, myEvaluationSettings.getValueSetCache().size()); + } + +} diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java index a5f4e7964a0..d822fe1ad1a 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.cr.r4; -import ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.MeasureReport; @@ -9,7 +8,7 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Resource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Optional; @@ -20,8 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(SpringExtension.class) class R4MeasureOperationProviderIT extends BaseCrR4TestServer { - @Autowired - MeasureOperationsProvider myMeasureOperationsProvider; public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){ @@ -44,7 +41,7 @@ class R4MeasureOperationProviderIT extends BaseCrR4TestServer { } @Test - void testMeasureEvaluate_EXM130() { + void testMeasureEvaluate_EXM130() throws InterruptedException { loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); } @@ -169,4 +166,5 @@ class R4MeasureOperationProviderIT extends BaseCrR4TestServer { } + } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java index d04be9cb71a..d3be5bab245 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java @@ -14,6 +14,7 @@ import org.cqframework.cql.cql2elm.model.Model; import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.execution.CqlEngine; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; @@ -25,6 +26,7 @@ import org.springframework.context.annotation.Primary; import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -63,10 +65,13 @@ public class TestCrR4Config { return measureEvalOptions; } @Bean - public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache) { + public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, + Map> theGlobalValueSetCache) { var evaluationSettings = EvaluationSettings.getDefault(); + var cqlOptions = evaluationSettings.getCqlOptions(); - var cqlEngineOptions = evaluationSettings.getEngineOptions(); + var cqlEngineOptions = cqlOptions.getCqlEngineOptions(); Set options = EnumSet.noneOf(CqlEngine.Options.class); if (theCqlProperties.isCqlRuntimeEnableExpressionCaching()) { options.add(CqlEngine.Options.EnableExpressionCaching); @@ -75,20 +80,61 @@ public class TestCrR4Config { options.add(CqlEngine.Options.EnableValidation); } cqlEngineOptions.setOptions(options); - var cqlOptions = evaluationSettings.getCqlOptions(); cqlOptions.setCqlEngineOptions(cqlEngineOptions); var cqlCompilerOptions = new CqlCompilerOptions(); - cqlCompilerOptions.setCompatibilityLevel("1.5"); - cqlOptions.setUseEmbeddedLibraries(true); - + if (theCqlProperties.isEnableDateRangeOptimization()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization); + } + if (theCqlProperties.isEnableAnnotations()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations); + } + if (theCqlProperties.isEnableLocators()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators); + } + if (theCqlProperties.isEnableResultsType()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes); + } + cqlCompilerOptions.setVerifyOnly(theCqlProperties.isCqlCompilerVerifyOnly()); + if (theCqlProperties.isEnableDetailedErrors()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors); + } + cqlCompilerOptions.setErrorLevel(theCqlProperties.getCqlCompilerErrorSeverityLevel()); + if (theCqlProperties.isDisableListTraversal()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal); + } + if (theCqlProperties.isDisableListDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion); + } + if (theCqlProperties.isDisableListPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion); + } + if (theCqlProperties.isEnableIntervalDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion); + } + if (theCqlProperties.isEnableIntervalPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion); + } + if (theCqlProperties.isDisableMethodInvocation()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation); + } + if (theCqlProperties.isRequireFromKeyword()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword); + } + cqlCompilerOptions.setValidateUnits(theCqlProperties.isCqlCompilerValidateUnits()); + if (theCqlProperties.isDisableDefaultModelInfoLoad()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad); + } + cqlCompilerOptions.setSignatureLevel(theCqlProperties.getCqlCompilerSignatureLevel()); + cqlCompilerOptions.setCompatibilityLevel(theCqlProperties.getCqlCompilerCompatibilityLevel()); cqlCompilerOptions.setAnalyzeDataRequirements(theCqlProperties.isCqlCompilerAnalyzeDataRequirements()); cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); + evaluationSettings.setValueSetCache(theGlobalValueSetCache); return evaluationSettings; } diff --git a/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json b/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json index b48340e557e..87864f5254d 100644 --- a/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json +++ b/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json @@ -147712,7 +147712,7 @@ } ], "library": [ - "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR" + "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR|0.0.001" ], "disclaimer": "The performance Measure is not a clinical guideline and does not establish a standard of medical care, and has not been tested for all potential applications. THE MEASURE AND SPECIFICATIONS ARE PROVIDED \"AS IS\" WITHOUT WARRANTY OF ANY KIND.\n \nDue to technical limitations, registered trademarks are indicated by (R) or [R] and unregistered trademarks are indicated by (TM) or [TM].", "scoring": { diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json new file mode 100644 index 00000000000..a5f8dd31414 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json @@ -0,0 +1,916 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle2", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ColorectalCancerScreeningsFHIR", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-library-cqfm" + ] + }, + "language": "en", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR", + "version": "0.0.001", + "name": "ColorectalCancerScreeningsFHIR", + "status": "active", + "experimental": false, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "date": "2023-10-03T15:32:03+00:00", + "publisher": "National Committee for Quality Assurance", + "description": "Colorectal Cancer ScreeningFHIR", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://ecqi.healthit.gov/ecqms/Library/FHIRHelpers|4.0.001" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://ecqi.healthit.gov/ecqms/Library/SupplementalDataElementsFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Global", + "resource": "http://ecqi.healthit.gov/ecqms/Library/MATGlobalCommonFunctionsFHIR4|6.0.000" + }, + { + "type": "depends-on", + "display": "Library AdultOutpatientEncounters", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdultOutpatientEncountersFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Hospice", + "resource": "http://ecqi.healthit.gov/ecqms/Library/HospiceFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Frailty", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdvancedIllnessandFrailtyExclusionECQMFHIR4|5.12.000" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT:2017-09", + "resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709" + }, + { + "type": "depends-on", + "display": "Value set Acute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1083" + }, + { + "type": "depends-on", + "display": "Value set Advanced Illness", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.110.12.1082" + }, + { + "type": "depends-on", + "display": "Value set Annual Wellness Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1240" + }, + { + "type": "depends-on", + "display": "Value set Care Services in Long-Term Residential Facility", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1014" + }, + { + "type": "depends-on", + "display": "Value set Colonoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "type": "depends-on", + "display": "Value set CT Colonography", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "type": "depends-on", + "display": "Value set Dementia Medications", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.196.12.1510" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Health Care Facility for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Home for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209" + }, + { + "type": "depends-on", + "display": "Value set ED", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1085" + }, + { + "type": "depends-on", + "display": "Value set Encounter Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + }, + { + "type": "depends-on", + "display": "Value set Fecal Occult Blood Test (FOBT)", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + }, + { + "type": "depends-on", + "display": "Value set FIT DNA", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + }, + { + "type": "depends-on", + "display": "Value set Flexible Sigmoidoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "type": "depends-on", + "display": "Value set Frailty Device", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + }, + { + "type": "depends-on", + "display": "Value set Frailty Diagnosis", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + }, + { + "type": "depends-on", + "display": "Value set Frailty Encounter", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + }, + { + "type": "depends-on", + "display": "Value set Frailty Symptom", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + }, + { + "type": "depends-on", + "display": "Value set Home Healthcare Services", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1016" + }, + { + "type": "depends-on", + "display": "Value set Hospice care ambulatory", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "type": "depends-on", + "display": "Value set Malignant Neoplasm of Colon", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Nonacute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1084" + }, + { + "type": "depends-on", + "display": "Value set Nursing Facility Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1012" + }, + { + "type": "depends-on", + "display": "Value set Observation", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1086" + }, + { + "type": "depends-on", + "display": "Value set Office Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Outpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1087" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services - Established Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1025" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services-Initial Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1023" + }, + { + "type": "depends-on", + "display": "Value set Total Colectomy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Any" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Flexible Sigmoidoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "CT Colonography Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Total Colectomy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Fecal Occult Blood Test Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Fecal Immunochemical Test DNA", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Colonoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Malignant Neoplasm", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Denominator Exclusion", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity" + } + ] + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "hospitalization", + "hospitalization.dischargeDisposition", + "type", + "status" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + } + ], + "dateFilter": [ + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "type" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + } + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "code", + "authoredOn", + "intent" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "DeviceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/DeviceRequest" + ], + "mustSupport": [ + "code", + "authoredOn" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMScNCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScNCg0KaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMDAxJyBjYWxsZWQgRkhJUkhlbHBlcnMNCmluY2x1ZGUgU3VwcGxlbWVudGFsRGF0YUVsZW1lbnRzRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIFNERQ0KaW5jbHVkZSBNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsDQppbmNsdWRlIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycw0KaW5jbHVkZSBIb3NwaWNlRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2UNCmluY2x1ZGUgQWR2YW5jZWRJbGxuZXNzYW5kRnJhaWx0eUV4Y2x1c2lvbkVDUU1GSElSNCB2ZXJzaW9uICc1LjEyLjAwMCcgY2FsbGVkIEZyYWlsdHkNCg0KY29kZXN5c3RlbSAiTE9JTkMiOiAnaHR0cDovL2xvaW5jLm9yZycNCg0KdmFsdWVzZXQgIkFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4MycNCnZhbHVlc2V0ICJBZHZhbmNlZCBJbGxuZXNzIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJw0KdmFsdWVzZXQgIkFubnVhbCBXZWxsbmVzcyBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJw0KdmFsdWVzZXQgIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0Jw0KdmFsdWVzZXQgIkNvbG9ub3Njb3B5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJw0KdmFsdWVzZXQgIkNUIENvbG9ub2dyYXBoeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCcNCnZhbHVlc2V0ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCcNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNycNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5Jw0KdmFsdWVzZXQgIkVEIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1Jw0KdmFsdWVzZXQgIkVuY291bnRlciBJbnBhdGllbnQiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3Jw0KdmFsdWVzZXQgIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IChGT0JUKSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMScNCnZhbHVlc2V0ICJGSVQgRE5BIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5Jw0KdmFsdWVzZXQgIkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEZXZpY2UiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEaWFnbm9zaXMiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQnDQp2YWx1ZXNldCAiRnJhaWx0eSBFbmNvdW50ZXIiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnDQp2YWx1ZXNldCAiRnJhaWx0eSBTeW1wdG9tIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1Jw0KdmFsdWVzZXQgIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNicNCnZhbHVlc2V0ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnDQp2YWx1ZXNldCAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJw0KdmFsdWVzZXQgIk5vbmFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCcNCnZhbHVlc2V0ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDEyJw0KdmFsdWVzZXQgIk9ic2VydmF0aW9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2Jw0KdmFsdWVzZXQgIk9mZmljZSBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMScNCnZhbHVlc2V0ICJPdXRwYXRpZW50IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg3Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJw0KdmFsdWVzZXQgIlRvdGFsIENvbGVjdG9teSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOScNCg0KY29kZSAiQmlydGggZGF0ZSI6ICcyMTExMi04JyBmcm9tICJMT0lOQyIgZGlzcGxheSAnQmlydGggZGF0ZScNCg0KcGFyYW1ldGVyICJNZWFzdXJlbWVudCBQZXJpb2QiIEludGVydmFsPERhdGVUaW1lPg0KICBkZWZhdWx0IEludGVydmFsW0AyMDE5LTAxLTAxVDAwOjAwOjAwLjAsIEAyMDIwLTAxLTAxVDAwOjAwOjAwLjApDQoNCmNvbnRleHQgUGF0aWVudA0KDQpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOg0KICBTREUuIlNERSBFdGhuaWNpdHkiDQoNCmRlZmluZSAiU0RFIFBheWVyIjoNCiAgU0RFLiJTREUgUGF5ZXIiDQoNCmRlZmluZSAiU0RFIFJhY2UiOg0KICBTREUuIlNERSBSYWNlIg0KDQpkZWZpbmUgIlNERSBTZXgiOg0KICBTREUuIlNERSBTZXgiDQoNCmRlZmluZSAiSW5pdGlhbCBQb3B1bGF0aW9uIjoNCiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSBiZXR3ZWVuIDUwIGFuZCA3NQ0KCSBhbmQgZXhpc3RzIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMuIlF1YWxpZnlpbmcgRW5jb3VudGVycyINCg0KZGVmaW5lICJEZW5vbWluYXRvciI6DQogICJJbml0aWFsIFBvcHVsYXRpb24iDQoNCmRlZmluZSAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiOg0KICBbUHJvY2VkdXJlOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSJdIEZsZXhpYmxlU2lnbW9pZG9zY29weQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZsZXhpYmxlU2lnbW9pZG9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZsZXhpYmxlU2lnbW9pZG9zY29weS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJDVCBDb2xvbm9ncmFwaHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNUIENvbG9ub2dyYXBoeSJdIENvbG9ub2dyYXBoeQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbG9ub2dyYXBoeS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbG9ub2dyYXBoeS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIlRvdGFsIENvbGVjdG9teSJdIENvbGVjdG9teQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbGVjdG9teS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbGVjdG9teS5wZXJmb3JtZWQpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIjoNCiAgW09ic2VydmF0aW9uOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIl0gRmVjYWxPY2N1bHRSZXN1bHQNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBGZWNhbE9jY3VsdFJlc3VsdC5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGZWNhbE9jY3VsdFJlc3VsdC52YWx1ZSBpcyBub3QgbnVsbA0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZlY2FsT2NjdWx0UmVzdWx0LmVmZmVjdGl2ZSkgZHVyaW5nIGRheSBvZiAiTWVhc3VyZW1lbnQgUGVyaW9kIg0KDQpkZWZpbmUgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIjoNCiAgW09ic2VydmF0aW9uOiAiRklUIEROQSJdIEZpdEROQQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZpdEROQS5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGaXRETkEudmFsdWUgaXMgbm90IG51bGwNCiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihGaXRETkEuZWZmZWN0aXZlKSBvY2N1cnMgMyB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBkYXkgb2YgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiQ29sb25vc2NvcHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNvbG9ub3Njb3B5Il0gQ29sb25vc2NvcHkNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBDb2xvbm9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCSAgYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihDb2xvbm9zY29weS5wZXJmb3JtZWQpIGVuZHMgMTAgeWVhcnMgb3IgbGVzcyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiTnVtZXJhdG9yIjoNCiAgZXhpc3RzICJDb2xvbm9zY29weSBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCINCiAgICAgICAgICAgICAgICAgICAgCQlvciBleGlzdHMgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIGV4aXN0cyAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCINCg0KZGVmaW5lICJNYWxpZ25hbnQgTmVvcGxhc20iOg0KICBbQ29uZGl0aW9uOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIl0gQ29sb3JlY3RhbENhbmNlcg0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEdsb2JhbC4iUHJldmFsZW5jZSBQZXJpb2QiKENvbG9yZWN0YWxDYW5jZXIpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRGVub21pbmF0b3IgRXhjbHVzaW9uIjoNCiAgSG9zcGljZS4iSGFzIEhvc3BpY2UiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJNYWxpZ25hbnQgTmVvcGxhc20iDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIEZyYWlsdHkuICJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwIg0KICAgICAgICAgICAgICAgICAgICAJCWFuZCBGcmFpbHR5LiJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiDQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxsaWJyYXJ5IHhtbG5zPSJ1cm46aGw3LW9yZzplbG06cjEiIHhtbG5zOnQ9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6Zmhpcj0iaHR0cDovL2hsNy5vcmcvZmhpciIgeG1sbnM6cWRtNDM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY0XzMiIHhtbG5zOnFkbTUzPSJ1cm46aGVhbHRoaXQtZ292OnFkbTp2NV8zIiB4bWxuczphPSJ1cm46aGw3LW9yZzpjcWwtYW5ub3RhdGlvbnM6cjEiPg0KICAgPGFubm90YXRpb24gdHJhbnNsYXRvclZlcnNpb249IjEuMyIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPg0KICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICA8YTpzIHI9IjIyNSI+DQogICAgICAgICA8YTpzPmxpYnJhcnkgQ29sb3JlY3RhbENhbmNlclNjcmVlbmluZ3NGSElSIHZlcnNpb24gJzAuMC4wMDEnPC9hOnM+DQogICAgICA8L2E6cz4NCiAgIDwvYW5ub3RhdGlvbj4NCiAgIDxpZGVudGlmaWVyIGlkPSJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiIHN5c3RlbT0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiB2ZXJzaW9uPSIwLjAuMDAxIi8+DQogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPg0KICAgPHVzaW5ncz4NCiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEiIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEiPg0KICAgICAgICAgICAgICAgPGE6cz51c2luZyA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkZISVI8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC91c2luZ3M+DQogICA8aW5jbHVkZXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIiIGxvY2F0b3I9IjU6MS01OjU2IiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4wMDEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIiPg0KICAgICAgICAgICAgICAgPGE6cz5pbmNsdWRlIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUkhlbHBlcnM8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjAwMScgY2FsbGVkIEZISVJIZWxwZXJzPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzIiBsb2NhdG9yPSI2OjEtNjo2NiIgbG9jYWxJZGVudGlmaWVyPSJTREUiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMyI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgU0RFPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI0IiBsb2NhdG9yPSI3OjEtNzo2OSIgbG9jYWxJZGVudGlmaWVyPSJHbG9iYWwiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgdmVyc2lvbj0iNi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNCI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI1IiBsb2NhdG9yPSI4OjEtODo4OSIgbG9jYWxJZGVudGlmaWVyPSJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyc0ZISVI0IiB2ZXJzaW9uPSIyLjAuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNiIgbG9jYXRvcj0iOToxLTk6NTMiIGxvY2FsSWRlbnRpZmllcj0iSG9zcGljZSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0hvc3BpY2VGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNiI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5Ib3NwaWNlRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2U8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjciIGxvY2F0b3I9IjEwOjEtMTA6ODUiIGxvY2FsSWRlbnRpZmllcj0iRnJhaWx0eSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIHZlcnNpb249IjUuMTIuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNS4xMi4wMDAnIGNhbGxlZCBGcmFpbHR5PC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC9pbmNsdWRlcz4NCiAgIDxwYXJhbWV0ZXJzPg0KICAgICAgPGRlZiBsb2NhbElkPSI0NSIgbG9jYXRvcj0iNDY6MS00Nzo2NiIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0NSI+DQogICAgICAgICAgICAgICA8YTpzPnBhcmFtZXRlciAmcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDsgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjQ0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWwmbHQ7PC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+RGF0ZVRpbWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4+PC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+CiAgZGVmYXVsdCA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNDIiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MCI+SW50ZXJ2YWxbQDIwMTktMDEtMDFUMDA6MDA6MDAuMCwgQDIwMjAtMDEtMDFUMDA6MDA6MDAuMCk8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZGVmYXVsdCBsb2NhbElkPSI0MiIgbG9jYXRvcj0iNDc6MTEtNDc6NjYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IGxvY2FsSWQ9IjQwIiBsb2NhdG9yPSI0NzoyMC00Nzo0MSIgeHNpOnR5cGU9IkRhdGVUaW1lIj4NCiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAxOSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8ZGF5IHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI0MSIgbG9jYXRvcj0iNDc6NDQtNDc6NjUiIHhzaTp0eXBlPSJEYXRlVGltZSI+DQogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bW9udGggdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgIDwvZGVmYXVsdD4NCiAgICAgICAgIDxwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQ0IiBsb2NhdG9yPSI0NjozMi00Njo0OSIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+DQogICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2FsSWQ9IjQzIiBsb2NhdG9yPSI0Njo0MS00Njo0OCIgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPg0KICAgICAgICAgPC9wYXJhbWV0ZXJUeXBlU3BlY2lmaWVyPg0KICAgICAgPC9kZWY+DQogICA8L3BhcmFtZXRlcnM+DQogICA8Y29kZVN5c3RlbXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjgiIGxvY2F0b3I9IjEyOjEtMTI6MzgiIG5hbWU9IkxPSU5DIiBpZD0iaHR0cDovL2xvaW5jLm9yZyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iOCI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGVzeXN0ZW0gJnF1b3Q7TE9JTkMmcXVvdDs6ICdodHRwOi8vbG9pbmMub3JnJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgIDwvY29kZVN5c3RlbXM+DQogICA8dmFsdWVTZXRzPg0KICAgICAgPGRlZiBsb2NhbElkPSI5IiBsb2NhdG9yPSIxNDoxLTE0OjEwNyIgbmFtZT0iQWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBY3V0ZSBJbnBhdGllbnQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Myc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEwIiBsb2NhdG9yPSIxNToxLTE1OjEwOCIgbmFtZT0iQWR2YW5jZWQgSWxsbmVzcyIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0FkdmFuY2VkIElsbG5lc3MmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4Mic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjExIiBsb2NhdG9yPSIxNjoxLTE2OjEwMyIgbmFtZT0iQW5udWFsIFdlbGxuZXNzIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNTI2LjMuMTI0MCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBbm51YWwgV2VsbG5lc3MgVmlzaXQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTIiIGxvY2F0b3I9IjE3OjEtMTc6MTM5IiBuYW1lPSJDYXJlIFNlcnZpY2VzIGluIExvbmctVGVybSBSZXNpZGVudGlhbCBGYWNpbGl0eSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMiI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0NhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMyIgbG9jYXRvcj0iMTg6MS0xODoxMDMiIG5hbWU9IkNvbG9ub3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMjAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q29sb25vc2NvcHkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAyMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0IiBsb2NhdG9yPSIxOToxLTE5OjEwNyIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q1QgQ29sb25vZ3JhcGh5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxNSIgbG9jYXRvcj0iMjA6MS0yMDoxMTIiIG5hbWU9IkRlbWVudGlhIE1lZGljYXRpb25zIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk2LjEyLjE1MTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGVtZW50aWEgTWVkaWNhdGlvbnMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2IiBsb2NhdG9yPSIyMToxLTIxOjEzNiIgbmFtZT0iRGlzY2hhcmdlZCB0byBIZWFsdGggQ2FyZSBGYWNpbGl0eSBmb3IgSG9zcGljZSBDYXJlIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTciIGxvY2F0b3I9IjIyOjEtMjI6MTIwIiBuYW1lPSJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTgiIGxvY2F0b3I9IjIzOjEtMjM6OTQiIG5hbWU9IkVEIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RUQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE5IiBsb2NhdG9yPSIyNDoxLTI0OjEwMCIgbmFtZT0iRW5jb3VudGVyIElucGF0aWVudCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjY2Ni41LjMwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtFbmNvdW50ZXIgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjAiIGxvY2F0b3I9IjI1OjEtMjU6MTIyIiBuYW1lPSJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjAiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIxIiBsb2NhdG9yPSIyNjoxLTI2Ojk5IiBuYW1lPSJGSVQgRE5BIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RklUIEROQSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjIiIGxvY2F0b3I9IjI3OjEtMjc6MTE0IiBuYW1lPSJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RmxleGlibGUgU2lnbW9pZG9zY29weSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjMiIGxvY2F0b3I9IjI4OjEtMjg6MTA2IiBuYW1lPSJGcmFpbHR5IERldmljZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExOC4xMi4xMzAwIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMyI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0ZyYWlsdHkgRGV2aWNlJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyNCIgbG9jYXRvcj0iMjk6MS0yOToxMDkiIG5hbWU9IkZyYWlsdHkgRGlhZ25vc2lzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RnJhaWx0eSBEaWFnbm9zaXMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI1IiBsb2NhdG9yPSIzMDoxLTMwOjEwOSIgbmFtZT0iRnJhaWx0eSBFbmNvdW50ZXIiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4OCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjUiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IEVuY291bnRlciZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg4JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjYiIGxvY2F0b3I9IjMxOjEtMzE6MTA3IiBuYW1lPSJGcmFpbHR5IFN5bXB0b20iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IFN5bXB0b20mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI3IiBsb2NhdG9yPSIzMjoxLTMyOjExNiIgbmFtZT0iSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7SG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyOCIgbG9jYXRvcj0iMzM6MS0zMzoxMDQiIG5hbWU9Ikhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyOCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0hvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjkiIGxvY2F0b3I9IjM0OjEtMzQ6MTE5IiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMwIiBsb2NhdG9yPSIzNToxLTM1OjExMCIgbmFtZT0iTm9uYWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMwIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Tm9uYWN1dGUgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMSIgbG9jYXRvcj0iMzY6MS0zNjoxMTQiIG5hbWU9Ik51cnNpbmcgRmFjaWxpdHkgVmlzaXQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtOdXJzaW5nIEZhY2lsaXR5IFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTInPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMiIgbG9jYXRvcj0iMzc6MS0zNzoxMDMiIG5hbWU9Ik9ic2VydmF0aW9uIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2JzZXJ2YXRpb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Nic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMzIiBsb2NhdG9yPSIzODoxLTM4OjEwNCIgbmFtZT0iT2ZmaWNlIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2ZmaWNlIFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNCIgbG9jYXRvcj0iMzk6MS0zOToxMDIiIG5hbWU9Ik91dHBhdGllbnQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzQiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtPdXRwYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODcnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNSIgbG9jYXRvcj0iNDA6MS00MDoxNTQiIG5hbWU9IlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjM1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7UHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyNSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjM2IiBsb2NhdG9yPSI0MToxLTQxOjE0OCIgbmFtZT0iUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzLUluaXRpYWwgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyMyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzciIGxvY2F0b3I9IjQyOjEtNDI6MTA3IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzciPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtUb3RhbCBDb2xlY3RvbXkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICA8L3ZhbHVlU2V0cz4NCiAgIDxjb2Rlcz4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzkiIGxvY2F0b3I9IjQ0OjEtNDQ6NjIiIG5hbWU9IkJpcnRoIGRhdGUiIGlkPSIyMTExMi04IiBkaXNwbGF5PSJCaXJ0aCBkYXRlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIzOSI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGUgJnF1b3Q7QmlydGggZGF0ZSZxdW90OzogJzIxMTEyLTgnIGZyb20gPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjM4Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TE9JTkMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gZGlzcGxheSAnQmlydGggZGF0ZSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYWxJZD0iMzgiIGxvY2F0b3I9IjQ0OjM1LTQ0OjQxIiBuYW1lPSJMT0lOQyIvPg0KICAgICAgPC9kZWY+DQogICA8L2NvZGVzPg0KICAgPHN0YXRlbWVudHM+DQogICAgICA8ZGVmIGxvY2F0b3I9IjQ5OjEtNDk6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDk6MS00OToxNSIgZGF0YVR5cGU9ImZoaXI6UGF0aWVudCIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiIHhzaTp0eXBlPSJSZXRyaWV2ZSIvPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjQ4IiBsb2NhdG9yPSI1MToxLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0OCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgRXRobmljaXR5JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBFdGhuaWNpdHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ3IiBsb2NhdG9yPSI1MjozLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjUxIiBsb2NhdG9yPSI1NDoxLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjUxIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O1NERSBQYXllciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUGF5ZXImcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUwIiBsb2NhdG9yPSI1NTozLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGxpYnJhcnlOYW1lPSJTREUiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNTQiIGxvY2F0b3I9IjU3OjEtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgUmFjZSZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUmFjZSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTMiIGxvY2F0b3I9IjU4OjMtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjU3IiBsb2NhdG9yPSI2MDoxLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NyI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgU2V4JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjU1Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBTZXgmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjU2IiBsb2NhdG9yPSI2MTozLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjY4IiBsb2NhdG9yPSI2MzoxLTY1OjYyIiBuYW1lPSJJbml0aWFsIFBvcHVsYXRpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjY4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0luaXRpYWwgUG9wdWxhdGlvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjciPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI2MyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWdlSW5ZZWFyc0F0KDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXJ0IG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYxIj4gYmV0d2VlbiA1MCBhbmQgNzU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KCSBhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjY2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtRdWFsaWZ5aW5nIEVuY291bnRlcnMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2NyIgbG9jYXRvcj0iNjQ6My02NTo2MiIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MyIgbG9jYXRvcj0iNjQ6My02NDo2MyIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iR3JlYXRlck9yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MSIgbG9jYXRvcj0iNjQ6NTUtNjQ6NTYiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNTAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iTGVzc09yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MiIgbG9jYXRvcj0iNjQ6NjItNjQ6NjMiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNzUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NiIgbG9jYXRvcj0iNjU6Ny02NTo2MiIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NSIgbG9jYXRvcj0iNjU6MTQtNjU6NjIiIG5hbWU9IlF1YWxpZnlpbmcgRW5jb3VudGVycyIgbGlicmFyeU5hbWU9IkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNzAiIGxvY2F0b3I9IjY3OjEtNjg6MjIiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3MCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtEZW5vbWluYXRvciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjkiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJbml0aWFsIFBvcHVsYXRpb24mcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2OSIgbG9jYXRvcj0iNjg6My02ODoyMiIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9Ijg3IiBsb2NhdG9yPSI3MDoxLTczOjE0OSIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9Ijg3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI4NiI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjcxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmxleGlibGVTaWdtb2lkb3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZsZXhpYmxlU2lnbW9pZG9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkdsb2JhbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GbGV4aWJsZVNpZ21vaWRvc2NvcHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz41IHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iODYiIGxvY2F0b3I9IjcxOjMtNzM6MTQ5IiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI3MiIgbG9jYXRvcj0iNzE6My03MTo2MSIgYWxpYXM9IkZsZXhpYmxlU2lnbW9pZG9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI3MSIgbG9jYXRvcj0iNzE6My03MTozOSIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI3MToxNS03MTozOCIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI4NSIgbG9jYXRvcj0iNzI6MjMtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijc2IiBsb2NhdG9yPSI3MjoyOS03Mjo3MCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NCIgbG9jYXRvcj0iNzI6MjktNzI6NTYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZsZXhpYmxlU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NSIgbG9jYXRvcj0iNzI6NjAtNzI6NzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4NCIgbG9jYXRvcj0iNzM6MjgtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjk0LTczOjEwOCIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjg5LTczOjkyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgwIiBsb2NhdG9yPSI3MzoyOC03Mzo4NyIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzkiIGxvY2F0b3I9IjczOjU2LTczOjg2IiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjczOjEyMy03MzoxNDkiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MyIgbG9jYXRvcj0iNzM6OTQtNzM6MTAwIiB2YWx1ZT0iNSIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3Mzo5NC03MzoxMDgiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgyIiBsb2NhdG9yPSI3MzoxMjMtNzM6MTQ5IiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgxIiBsb2NhdG9yPSI3MzoxMzAtNzM6MTQ5IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTA0IiBsb2NhdG9yPSI3NToxLTc4OjE0MCIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTA0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0NUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjEwMyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4OSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NUIENvbG9ub2dyYXBoeSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwMiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Db2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub2dyYXBoeTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+NSB5ZWFyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBvciBsZXNzIG9uIG9yIGJlZm9yZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEwMyIgbG9jYXRvcj0iNzY6My03ODoxNDAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijg5IiBsb2NhdG9yPSI3NjozLTc2OjQ1IiBhbGlhcz0iQ29sb25vZ3JhcGh5Ij4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9Ijg4IiBsb2NhdG9yPSI3NjozLTc2OjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9Ijc2OjE1LTc2OjMxIiBuYW1lPSJDVCBDb2xvbm9ncmFwaHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTAyIiBsb2NhdG9yPSI3NzoyMy03ODoxNDAiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTMiIGxvY2F0b3I9Ijc3OjI5LTc3OjYxIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkxIiBsb2NhdG9yPSI3NzoyOS03Nzo0NyIgcGF0aD0ic3RhdHVzIiBzY29wZT0iQ29sb25vZ3JhcGh5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkyIiBsb2NhdG9yPSI3Nzo1MS03Nzo2MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwMSIgbG9jYXRvcj0iNzg6MjgtNzg6MTQwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc4Ojg1LTc4Ojk5IiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODAtNzg6ODMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTciIGxvY2F0b3I9Ijc4OjI4LTc4Ojc4IiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5NiIgbG9jYXRvcj0iNzg6NTYtNzg6NzciIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IkNvbG9ub2dyYXBoeSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3ODo4NS03ODo5OSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI3ODoxMTQtNzg6MTQwIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTAwIiBsb2NhdG9yPSI3ODo4NS03ODo5MSIgdmFsdWU9IjUiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMjAiIGxvY2F0b3I9IjgwOjEtODM6MTIzIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMjAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTE5Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215JnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExOCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTciPiBzdGFydHMgb24gb3IgYmVmb3JlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxMTkiIGxvY2F0b3I9IjgxOjMtODM6MTIzIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMDYiIGxvY2F0b3I9IjgxOjMtODE6NDIiIGFsaWFzPSJDb2xlY3RvbXkiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTA1IiBsb2NhdG9yPSI4MTozLTgxOjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9IjgxOjE1LTgxOjMxIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTE4IiBsb2NhdG9yPSI4MjoyMy04MzoxMjMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEwIiBsb2NhdG9yPSI4MjoyOS04Mjo1OCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMDgiIGxvY2F0b3I9IjgyOjI5LTgyOjQ0IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJDb2xlY3RvbXkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTA5IiBsb2NhdG9yPSI4Mjo0OC04Mjo1OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNyIgbG9jYXRvcj0iODM6MjgtODM6MTIzIiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjc3LTgzOjgyIiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTE0IiBsb2NhdG9yPSI4MzoyOC04Mzo3NSIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEzIiBsb2NhdG9yPSI4Mzo1Ni04Mzo3NCIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sZWN0b215IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNiIgbG9jYXRvcj0iODM6OTctODM6MTIzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNSIgbG9jYXRvcj0iODM6MTA0LTgzOjEyMyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0MyIgbG9jYXRvcj0iODU6MS04OToxMTgiIG5hbWU9IkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTQzIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTQyIj4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltPYnNlcnZhdGlvbjogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GZWNhbE9jY3VsdFJlc3VsdDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaW4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPns8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2ZpbmFsJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhbWVuZGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb3JyZWN0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J3ByZWxpbWluYXJ5JzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPn08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbm90IG51bGw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZlY2FsT2NjdWx0UmVzdWx0PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+IGR1cmluZyBkYXkgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTQyIiBsb2NhdG9yPSI4NjozLTg5OjExOCIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTIyIiBsb2NhdG9yPSI4NjozLTg2OjY3IiBhbGlhcz0iRmVjYWxPY2N1bHRSZXN1bHQiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTIxIiBsb2NhdG9yPSI4NjozLTg2OjQ5IiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI4NjoxNy04Njo0OCIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE0MSIgbG9jYXRvcj0iODc6MjMtODk6MTE4IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzNCIgbG9jYXRvcj0iODc6MjktODg6NjIiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMwIiBsb2NhdG9yPSI4NzoyOS04NzoxMDQiIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI0IiBsb2NhdG9yPSI4NzoyOS04Nzo1MiIgcGF0aD0ic3RhdHVzIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI5IiBsb2NhdG9yPSI4Nzo1Ny04NzoxMDQiIHhzaTp0eXBlPSJMaXN0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNSIgbG9jYXRvcj0iODc6NTgtODc6NjQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNiIgbG9jYXRvcj0iODc6NjctODc6NzUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJhbWVuZGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iMTI3IiBsb2NhdG9yPSI4Nzo3OC04Nzo4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvcnJlY3RlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyOCIgbG9jYXRvcj0iODc6OTEtODc6MTAzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzMiIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzMiIgbG9jYXRvcj0iODg6MjgtODg6NTAiIHBhdGg9InZhbHVlIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTQwIiBsb2NhdG9yPSI4OToyOC04OToxMTgiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iSW5jbHVkZWRJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzgiIGxvY2F0b3I9Ijg5OjI4LTg5OjgzIiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzciIGxvY2F0b3I9Ijg5OjU2LTg5OjgyIiBwYXRoPSJlZmZlY3RpdmUiIHNjb3BlPSJGZWNhbE9jY3VsdFJlc3VsdCIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzkiIGxvY2F0b3I9Ijg5Ojk5LTg5OjExOCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2OCIgbG9jYXRvcj0iOTE6MS05NToxNDMiIG5hbWU9IkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxNjgiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7RmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjE2NyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7RklUIEROQSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBGaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGluIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz57PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidmaW5hbCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYW1lbmRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29ycmVjdGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidwcmVsaW1pbmFyeSc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz59PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZpdEROQTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyBub3QgbnVsbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5HbG9iYWw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Rml0RE5BPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+b2NjdXJzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTY3IiBsb2NhdG9yPSI5MjozLTk1OjE0MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTQ1IiBsb2NhdG9yPSI5MjozLTkyOjMzIiBhbGlhcz0iRml0RE5BIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE0NCIgbG9jYXRvcj0iOTI6My05MjoyNiIgZGF0YVR5cGU9ImZoaXI6T2JzZXJ2YXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9PYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTI6MTctOTI6MjUiIG5hbWU9IkZJVCBETkEiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTY2IiBsb2NhdG9yPSI5MzoyMy05NToxNDMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTU3IiBsb2NhdG9yPSI5MzoyOS05NDo1MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTMiIGxvY2F0b3I9IjkzOjI5LTkzOjkzIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0NyIgbG9jYXRvcj0iOTM6MjktOTM6NDEiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTIiIGxvY2F0b3I9IjkzOjQ2LTkzOjkzIiB4c2k6dHlwZT0iTGlzdCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDgiIGxvY2F0b3I9IjkzOjQ3LTkzOjUzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDkiIGxvY2F0b3I9IjkzOjU2LTkzOjY0IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYW1lbmRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjE1MCIgbG9jYXRvcj0iOTM6NjctOTM6NzciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb3JyZWN0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNTEiIGxvY2F0b3I9IjkzOjgwLTkzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTYiIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE1NSIgbG9jYXRvcj0iOTQ6MjgtOTQ6MzkiIHBhdGg9InZhbHVlIiBzY29wZT0iRml0RE5BIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2NSIgbG9jYXRvcj0iOTU6MjgtOTU6MTQzIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjM1LTk1OjcyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MSIgbG9jYXRvcj0iOTU6MjgtOTU6NzIiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MCIgbG9jYXRvcj0iOTU6NTYtOTU6NzEiIHBhdGg9ImVmZmVjdGl2ZSIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NTo4MS05NTo5NSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYzIiBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MiIgbG9jYXRvcj0iOTU6MTI0LTk1OjE0MyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjQiIGxvY2F0b3I9Ijk1OjgxLTk1Ojg3IiB2YWx1ZT0iMyIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIxNjMiIGxvY2F0b3I9Ijk1OjExNy05NToxNDMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYyIiBsb2NhdG9yPSI5NToxMjQtOTU6MTQzIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MyIgbG9jYXRvcj0iOTU6MTE3LTk1OjE0MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjIiIGxvY2F0b3I9Ijk1OjEyNC05NToxNDMiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxODUiIGxvY2F0b3I9Ijk3OjEtMTAwOjE0MSIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxODUiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7Q29sb25vc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxODQiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTcwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtDb2xvbm9zY29weSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkgIGFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MTAgeWVhcnM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gb3IgbGVzcyBvbiBvciBiZWZvcmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE4NCIgbG9jYXRvcj0iOTg6My0xMDA6MTQxIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxNzAiIGxvY2F0b3I9Ijk4OjMtOTg6NDAiIGFsaWFzPSJDb2xvbm9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxNjkiIGxvY2F0b3I9Ijk4OjMtOTg6MjgiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTg6MTUtOTg6MjciIG5hbWU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE4MyIgbG9jYXRvcj0iOTk6MjMtMTAwOjE0MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzQiIGxvY2F0b3I9Ijk5OjI5LTk5OjYwIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MiIgbG9jYXRvcj0iOTk6MjktOTk6NDYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MyIgbG9jYXRvcj0iOTk6NTAtOTk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxODIiIGxvY2F0b3I9IjEwMDoyOS0xMDA6MTQxIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEwMDo4NS0xMDA6MTAwIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjgwLTEwMDo4MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzgiIGxvY2F0b3I9IjEwMDoyOS0xMDA6NzgiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3NyIgbG9jYXRvcj0iMTAwOjU3LTEwMDo3NyIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sb25vc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTAwOjExNS0xMDA6MTQxIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgxIiBsb2NhdG9yPSIxMDA6ODUtMTAwOjkyIiB2YWx1ZT0iMTAiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyMDAiIGxvY2F0b3I9IjEwMjoxLTEwNzo1OSIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMDAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7TnVtZXJhdG9yJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxOTkiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTYiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NvbG9ub3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTkyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5OCI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7Q1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxOTkiIGxvY2F0b3I9IjEwMzozLTEwNzo1OSIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5NiIgbG9jYXRvcj0iMTAzOjMtMTA2OjYzIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkzIiBsb2NhdG9yPSIxMDM6My0xMDU6NjYiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTAiIGxvY2F0b3I9IjEwMzozLTEwNDo2NyIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NyIgbG9jYXRvcj0iMTAzOjMtMTAzOjMyIiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NiIgbG9jYXRvcj0iMTAzOjEwLTEwMzozMiIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg5IiBsb2NhdG9yPSIxMDQ6MjYtMTA0OjY3IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4OCIgbG9jYXRvcj0iMTA0OjMzLTEwNDo2NyIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkyIiBsb2NhdG9yPSIxMDU6MjYtMTA1OjY2IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5MSIgbG9jYXRvcj0iMTA1OjMzLTEwNTo2NiIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTUiIGxvY2F0b3I9IjEwNjoyNi0xMDY6NjMiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTk0IiBsb2NhdG9yPSIxMDY6MzMtMTA2OjYzIiBuYW1lPSJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5OCIgbG9jYXRvcj0iMTA3OjI2LTEwNzo1OSIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTciIGxvY2F0b3I9IjEwNzozMy0xMDc6NTkiIG5hbWU9IkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjEwIiBsb2NhdG9yPSIxMDk6MS0xMTE6MTIwIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxMCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20mcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjIwOSI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bQ29uZGl0aW9uOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01hbGlnbmFudCBOZW9wbGFzbSBvZiBDb2xvbiZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvcmVjdGFsQ2FuY2VyPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDgiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtQcmV2YWxlbmNlIFBlcmlvZCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q29sb3JlY3RhbENhbmNlcjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwOCI+IHN0YXJ0cyBvbiBvciBiZWZvcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDkiIGxvY2F0b3I9IjExMDozLTExMToxMjAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjIwMiIgbG9jYXRvcj0iMTEwOjMtMTEwOjYxIiBhbGlhcz0iQ29sb3JlY3RhbENhbmNlciI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDEiIGxvY2F0b3I9IjExMDozLTExMDo0NCIgZGF0YVR5cGU9ImZoaXI6Q29uZGl0aW9uIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSIxMTA6MTUtMTEwOjQzIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMjA4IiBsb2NhdG9yPSIxMTE6MjMtMTExOjEyMCIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTE6NzQtMTExOjc5IiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjA1IiBsb2NhdG9yPSIxMTE6MjktMTExOjcyIiBuYW1lPSJQcmV2YWxlbmNlIFBlcmlvZCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNCIgbG9jYXRvcj0iMTExOjU2LTExMTo3MSIgbmFtZT0iQ29sb3JlY3RhbENhbmNlciIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMDciIGxvY2F0b3I9IjExMTo5NC0xMTE6MTIwIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNiIgbG9jYXRvcj0iMTExOjEwMS0xMTE6MTIwIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjI1IiBsb2NhdG9yPSIxMTM6MS0xMTg6OTAiIG5hbWU9IkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0Rlbm9taW5hdG9yIEV4Y2x1c2lvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMjI0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+SG9zcGljZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGFzIEhvc3BpY2UmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWFsaWduYW50IE5lb3BsYXNtJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GcmFpbHR5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7QWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZyYWlsdHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0xvbmcgVGVybSBDYXJlIFBlcmlvZHMgTG9uZ2VyIFRoYW4gOTAgQ29uc2VjdXRpdmUgRGF5cyZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjIyNCIgbG9jYXRvcj0iMTE0OjMtMTE4OjkwIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE4IiBsb2NhdG9yPSIxMTQ6My0xMTY6NTkiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTUiIGxvY2F0b3I9IjExNDozLTExNTo1MiIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIxMiIgbG9jYXRvcj0iMTE0OjMtMTE0OjIzIiBuYW1lPSJIYXMgSG9zcGljZSIgbGlicmFyeU5hbWU9Ikhvc3BpY2UiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTQiIGxvY2F0b3I9IjExNToyNi0xMTU6NTIiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjEzIiBsb2NhdG9yPSIxMTU6MzMtMTE1OjUyIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTciIGxvY2F0b3I9IjExNjoyNi0xMTY6NTkiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE2IiBsb2NhdG9yPSIxMTY6MzMtMTE2OjU5IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIzIiBsb2NhdG9yPSIxMTc6MjYtMTE4OjkwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyMCIgbG9jYXRvcj0iMTE3OjI2LTExNzoxMDAiIG5hbWU9IkFkdmFuY2VkIElsbG5lc3MgYW5kIEZyYWlsdHkgRXhjbHVzaW9uIE5vdCBJbmNsdWRpbmcgT3ZlciBBZ2UgODAiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIyIiBsb2NhdG9yPSIxMTg6MjctMTE4OjkwIiBuYW1lPSJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICA8L3N0YXRlbWVudHM+DQo8L2xpYnJhcnk+DQo=" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ColorectalCancerScreeningsFHIR", + "ifNoneExist": "Library?url=http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR&version=0.0.001" + } + } + ]} diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json new file mode 100644 index 00000000000..d3cba496293 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json @@ -0,0 +1,920 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ColorectalCancerScreeningsFHIR", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-library-cqfm" + ] + }, + "language": "en", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR", + "version": "0.0.002", + "name": "ColorectalCancerScreeningsFHIR", + "status": "active", + "experimental": false, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "date": "2021-04-23T15:32:03+00:00", + "publisher": "National Committee for Quality Assurance", + "description": "Colorectal Cancer ScreeningFHIR", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://ecqi.healthit.gov/ecqms/Library/FHIRHelpers|4.0.001" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://ecqi.healthit.gov/ecqms/Library/SupplementalDataElementsFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Global", + "resource": "http://ecqi.healthit.gov/ecqms/Library/MATGlobalCommonFunctionsFHIR4|6.0.000" + }, + { + "type": "depends-on", + "display": "Library AdultOutpatientEncounters", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdultOutpatientEncountersFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Hospice", + "resource": "http://ecqi.healthit.gov/ecqms/Library/HospiceFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Frailty", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdvancedIllnessandFrailtyExclusionECQMFHIR4|5.12.000" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT:2017-09", + "resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709" + }, + { + "type": "depends-on", + "display": "Value set Acute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1083" + }, + { + "type": "depends-on", + "display": "Value set Advanced Illness", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.110.12.1082" + }, + { + "type": "depends-on", + "display": "Value set Annual Wellness Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1240" + }, + { + "type": "depends-on", + "display": "Value set Care Services in Long-Term Residential Facility", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1014" + }, + { + "type": "depends-on", + "display": "Value set Colonoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "type": "depends-on", + "display": "Value set CT Colonography", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "type": "depends-on", + "display": "Value set Dementia Medications", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.196.12.1510" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Health Care Facility for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Home for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209" + }, + { + "type": "depends-on", + "display": "Value set ED", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1085" + }, + { + "type": "depends-on", + "display": "Value set Encounter Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + }, + { + "type": "depends-on", + "display": "Value set Fecal Occult Blood Test (FOBT)", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + }, + { + "type": "depends-on", + "display": "Value set FIT DNA", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + }, + { + "type": "depends-on", + "display": "Value set Flexible Sigmoidoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "type": "depends-on", + "display": "Value set Frailty Device", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + }, + { + "type": "depends-on", + "display": "Value set Frailty Diagnosis", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + }, + { + "type": "depends-on", + "display": "Value set Frailty Encounter", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + }, + { + "type": "depends-on", + "display": "Value set Frailty Symptom", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + }, + { + "type": "depends-on", + "display": "Value set Home Healthcare Services", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1016" + }, + { + "type": "depends-on", + "display": "Value set Hospice care ambulatory", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "type": "depends-on", + "display": "Value set Malignant Neoplasm of Colon", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Nonacute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1084" + }, + { + "type": "depends-on", + "display": "Value set Nursing Facility Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1012" + }, + { + "type": "depends-on", + "display": "Value set Observation", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1086" + }, + { + "type": "depends-on", + "display": "Value set Office Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Outpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1087" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services - Established Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1025" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services-Initial Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1023" + }, + { + "type": "depends-on", + "display": "Value set Total Colectomy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Any" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Flexible Sigmoidoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "CT Colonography Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Total Colectomy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Fecal Occult Blood Test Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Fecal Immunochemical Test DNA", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Colonoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Malignant Neoplasm", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Denominator Exclusion", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity" + } + ] + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "hospitalization", + "hospitalization.dischargeDisposition", + "type", + "status" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + } + ], + "dateFilter": [ + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "type" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + } + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "code", + "authoredOn", + "intent" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "DeviceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/DeviceRequest" + ], + "mustSupport": [ + "code", + "authoredOn" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMScNCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScNCg0KaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMDAxJyBjYWxsZWQgRkhJUkhlbHBlcnMNCmluY2x1ZGUgU3VwcGxlbWVudGFsRGF0YUVsZW1lbnRzRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIFNERQ0KaW5jbHVkZSBNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsDQppbmNsdWRlIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycw0KaW5jbHVkZSBIb3NwaWNlRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2UNCmluY2x1ZGUgQWR2YW5jZWRJbGxuZXNzYW5kRnJhaWx0eUV4Y2x1c2lvbkVDUU1GSElSNCB2ZXJzaW9uICc1LjEyLjAwMCcgY2FsbGVkIEZyYWlsdHkNCg0KY29kZXN5c3RlbSAiTE9JTkMiOiAnaHR0cDovL2xvaW5jLm9yZycNCg0KdmFsdWVzZXQgIkFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4MycNCnZhbHVlc2V0ICJBZHZhbmNlZCBJbGxuZXNzIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJw0KdmFsdWVzZXQgIkFubnVhbCBXZWxsbmVzcyBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJw0KdmFsdWVzZXQgIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0Jw0KdmFsdWVzZXQgIkNvbG9ub3Njb3B5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJw0KdmFsdWVzZXQgIkNUIENvbG9ub2dyYXBoeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCcNCnZhbHVlc2V0ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCcNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNycNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5Jw0KdmFsdWVzZXQgIkVEIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1Jw0KdmFsdWVzZXQgIkVuY291bnRlciBJbnBhdGllbnQiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3Jw0KdmFsdWVzZXQgIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IChGT0JUKSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMScNCnZhbHVlc2V0ICJGSVQgRE5BIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5Jw0KdmFsdWVzZXQgIkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEZXZpY2UiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEaWFnbm9zaXMiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQnDQp2YWx1ZXNldCAiRnJhaWx0eSBFbmNvdW50ZXIiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnDQp2YWx1ZXNldCAiRnJhaWx0eSBTeW1wdG9tIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1Jw0KdmFsdWVzZXQgIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNicNCnZhbHVlc2V0ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnDQp2YWx1ZXNldCAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJw0KdmFsdWVzZXQgIk5vbmFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCcNCnZhbHVlc2V0ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDEyJw0KdmFsdWVzZXQgIk9ic2VydmF0aW9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2Jw0KdmFsdWVzZXQgIk9mZmljZSBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMScNCnZhbHVlc2V0ICJPdXRwYXRpZW50IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg3Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJw0KdmFsdWVzZXQgIlRvdGFsIENvbGVjdG9teSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOScNCg0KY29kZSAiQmlydGggZGF0ZSI6ICcyMTExMi04JyBmcm9tICJMT0lOQyIgZGlzcGxheSAnQmlydGggZGF0ZScNCg0KcGFyYW1ldGVyICJNZWFzdXJlbWVudCBQZXJpb2QiIEludGVydmFsPERhdGVUaW1lPg0KICBkZWZhdWx0IEludGVydmFsW0AyMDE5LTAxLTAxVDAwOjAwOjAwLjAsIEAyMDIwLTAxLTAxVDAwOjAwOjAwLjApDQoNCmNvbnRleHQgUGF0aWVudA0KDQpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOg0KICBTREUuIlNERSBFdGhuaWNpdHkiDQoNCmRlZmluZSAiU0RFIFBheWVyIjoNCiAgU0RFLiJTREUgUGF5ZXIiDQoNCmRlZmluZSAiU0RFIFJhY2UiOg0KICBTREUuIlNERSBSYWNlIg0KDQpkZWZpbmUgIlNERSBTZXgiOg0KICBTREUuIlNERSBTZXgiDQoNCmRlZmluZSAiSW5pdGlhbCBQb3B1bGF0aW9uIjoNCiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSBiZXR3ZWVuIDUwIGFuZCA3NQ0KCSBhbmQgZXhpc3RzIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMuIlF1YWxpZnlpbmcgRW5jb3VudGVycyINCg0KZGVmaW5lICJEZW5vbWluYXRvciI6DQogICJJbml0aWFsIFBvcHVsYXRpb24iDQoNCmRlZmluZSAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiOg0KICBbUHJvY2VkdXJlOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSJdIEZsZXhpYmxlU2lnbW9pZG9zY29weQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZsZXhpYmxlU2lnbW9pZG9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZsZXhpYmxlU2lnbW9pZG9zY29weS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJDVCBDb2xvbm9ncmFwaHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNUIENvbG9ub2dyYXBoeSJdIENvbG9ub2dyYXBoeQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbG9ub2dyYXBoeS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbG9ub2dyYXBoeS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIlRvdGFsIENvbGVjdG9teSJdIENvbGVjdG9teQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbGVjdG9teS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbGVjdG9teS5wZXJmb3JtZWQpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIjoNCiAgW09ic2VydmF0aW9uOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIl0gRmVjYWxPY2N1bHRSZXN1bHQNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBGZWNhbE9jY3VsdFJlc3VsdC5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGZWNhbE9jY3VsdFJlc3VsdC52YWx1ZSBpcyBub3QgbnVsbA0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZlY2FsT2NjdWx0UmVzdWx0LmVmZmVjdGl2ZSkgZHVyaW5nIGRheSBvZiAiTWVhc3VyZW1lbnQgUGVyaW9kIg0KDQpkZWZpbmUgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIjoNCiAgW09ic2VydmF0aW9uOiAiRklUIEROQSJdIEZpdEROQQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZpdEROQS5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGaXRETkEudmFsdWUgaXMgbm90IG51bGwNCiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihGaXRETkEuZWZmZWN0aXZlKSBvY2N1cnMgMyB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBkYXkgb2YgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiQ29sb25vc2NvcHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNvbG9ub3Njb3B5Il0gQ29sb25vc2NvcHkNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBDb2xvbm9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCSAgYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihDb2xvbm9zY29weS5wZXJmb3JtZWQpIGVuZHMgMTAgeWVhcnMgb3IgbGVzcyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiTnVtZXJhdG9yIjoNCiAgZXhpc3RzICJDb2xvbm9zY29weSBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCINCiAgICAgICAgICAgICAgICAgICAgCQlvciBleGlzdHMgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIGV4aXN0cyAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCINCg0KZGVmaW5lICJNYWxpZ25hbnQgTmVvcGxhc20iOg0KICBbQ29uZGl0aW9uOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIl0gQ29sb3JlY3RhbENhbmNlcg0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEdsb2JhbC4iUHJldmFsZW5jZSBQZXJpb2QiKENvbG9yZWN0YWxDYW5jZXIpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRGVub21pbmF0b3IgRXhjbHVzaW9uIjoNCiAgSG9zcGljZS4iSGFzIEhvc3BpY2UiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJNYWxpZ25hbnQgTmVvcGxhc20iDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIEZyYWlsdHkuICJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwIg0KICAgICAgICAgICAgICAgICAgICAJCWFuZCBGcmFpbHR5LiJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiDQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxsaWJyYXJ5IHhtbG5zPSJ1cm46aGw3LW9yZzplbG06cjEiIHhtbG5zOnQ9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6Zmhpcj0iaHR0cDovL2hsNy5vcmcvZmhpciIgeG1sbnM6cWRtNDM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY0XzMiIHhtbG5zOnFkbTUzPSJ1cm46aGVhbHRoaXQtZ292OnFkbTp2NV8zIiB4bWxuczphPSJ1cm46aGw3LW9yZzpjcWwtYW5ub3RhdGlvbnM6cjEiPg0KICAgPGFubm90YXRpb24gdHJhbnNsYXRvclZlcnNpb249IjEuMyIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPg0KICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICA8YTpzIHI9IjIyNSI+DQogICAgICAgICA8YTpzPmxpYnJhcnkgQ29sb3JlY3RhbENhbmNlclNjcmVlbmluZ3NGSElSIHZlcnNpb24gJzAuMC4wMDEnPC9hOnM+DQogICAgICA8L2E6cz4NCiAgIDwvYW5ub3RhdGlvbj4NCiAgIDxpZGVudGlmaWVyIGlkPSJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiIHN5c3RlbT0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiB2ZXJzaW9uPSIwLjAuMDAxIi8+DQogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPg0KICAgPHVzaW5ncz4NCiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEiIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEiPg0KICAgICAgICAgICAgICAgPGE6cz51c2luZyA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkZISVI8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC91c2luZ3M+DQogICA8aW5jbHVkZXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIiIGxvY2F0b3I9IjU6MS01OjU2IiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4wMDEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIiPg0KICAgICAgICAgICAgICAgPGE6cz5pbmNsdWRlIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUkhlbHBlcnM8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjAwMScgY2FsbGVkIEZISVJIZWxwZXJzPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzIiBsb2NhdG9yPSI2OjEtNjo2NiIgbG9jYWxJZGVudGlmaWVyPSJTREUiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMyI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgU0RFPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI0IiBsb2NhdG9yPSI3OjEtNzo2OSIgbG9jYWxJZGVudGlmaWVyPSJHbG9iYWwiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgdmVyc2lvbj0iNi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNCI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI1IiBsb2NhdG9yPSI4OjEtODo4OSIgbG9jYWxJZGVudGlmaWVyPSJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyc0ZISVI0IiB2ZXJzaW9uPSIyLjAuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNiIgbG9jYXRvcj0iOToxLTk6NTMiIGxvY2FsSWRlbnRpZmllcj0iSG9zcGljZSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0hvc3BpY2VGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNiI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5Ib3NwaWNlRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2U8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjciIGxvY2F0b3I9IjEwOjEtMTA6ODUiIGxvY2FsSWRlbnRpZmllcj0iRnJhaWx0eSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIHZlcnNpb249IjUuMTIuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNS4xMi4wMDAnIGNhbGxlZCBGcmFpbHR5PC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC9pbmNsdWRlcz4NCiAgIDxwYXJhbWV0ZXJzPg0KICAgICAgPGRlZiBsb2NhbElkPSI0NSIgbG9jYXRvcj0iNDY6MS00Nzo2NiIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0NSI+DQogICAgICAgICAgICAgICA8YTpzPnBhcmFtZXRlciAmcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDsgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjQ0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWwmbHQ7PC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+RGF0ZVRpbWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4+PC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+CiAgZGVmYXVsdCA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNDIiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MCI+SW50ZXJ2YWxbQDIwMTktMDEtMDFUMDA6MDA6MDAuMCwgQDIwMjAtMDEtMDFUMDA6MDA6MDAuMCk8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZGVmYXVsdCBsb2NhbElkPSI0MiIgbG9jYXRvcj0iNDc6MTEtNDc6NjYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IGxvY2FsSWQ9IjQwIiBsb2NhdG9yPSI0NzoyMC00Nzo0MSIgeHNpOnR5cGU9IkRhdGVUaW1lIj4NCiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAxOSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8ZGF5IHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI0MSIgbG9jYXRvcj0iNDc6NDQtNDc6NjUiIHhzaTp0eXBlPSJEYXRlVGltZSI+DQogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bW9udGggdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgIDwvZGVmYXVsdD4NCiAgICAgICAgIDxwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQ0IiBsb2NhdG9yPSI0NjozMi00Njo0OSIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+DQogICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2FsSWQ9IjQzIiBsb2NhdG9yPSI0Njo0MS00Njo0OCIgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPg0KICAgICAgICAgPC9wYXJhbWV0ZXJUeXBlU3BlY2lmaWVyPg0KICAgICAgPC9kZWY+DQogICA8L3BhcmFtZXRlcnM+DQogICA8Y29kZVN5c3RlbXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjgiIGxvY2F0b3I9IjEyOjEtMTI6MzgiIG5hbWU9IkxPSU5DIiBpZD0iaHR0cDovL2xvaW5jLm9yZyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iOCI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGVzeXN0ZW0gJnF1b3Q7TE9JTkMmcXVvdDs6ICdodHRwOi8vbG9pbmMub3JnJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgIDwvY29kZVN5c3RlbXM+DQogICA8dmFsdWVTZXRzPg0KICAgICAgPGRlZiBsb2NhbElkPSI5IiBsb2NhdG9yPSIxNDoxLTE0OjEwNyIgbmFtZT0iQWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBY3V0ZSBJbnBhdGllbnQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Myc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEwIiBsb2NhdG9yPSIxNToxLTE1OjEwOCIgbmFtZT0iQWR2YW5jZWQgSWxsbmVzcyIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0FkdmFuY2VkIElsbG5lc3MmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4Mic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjExIiBsb2NhdG9yPSIxNjoxLTE2OjEwMyIgbmFtZT0iQW5udWFsIFdlbGxuZXNzIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNTI2LjMuMTI0MCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBbm51YWwgV2VsbG5lc3MgVmlzaXQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTIiIGxvY2F0b3I9IjE3OjEtMTc6MTM5IiBuYW1lPSJDYXJlIFNlcnZpY2VzIGluIExvbmctVGVybSBSZXNpZGVudGlhbCBGYWNpbGl0eSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMiI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0NhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMyIgbG9jYXRvcj0iMTg6MS0xODoxMDMiIG5hbWU9IkNvbG9ub3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMjAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q29sb25vc2NvcHkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAyMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0IiBsb2NhdG9yPSIxOToxLTE5OjEwNyIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q1QgQ29sb25vZ3JhcGh5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxNSIgbG9jYXRvcj0iMjA6MS0yMDoxMTIiIG5hbWU9IkRlbWVudGlhIE1lZGljYXRpb25zIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk2LjEyLjE1MTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGVtZW50aWEgTWVkaWNhdGlvbnMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2IiBsb2NhdG9yPSIyMToxLTIxOjEzNiIgbmFtZT0iRGlzY2hhcmdlZCB0byBIZWFsdGggQ2FyZSBGYWNpbGl0eSBmb3IgSG9zcGljZSBDYXJlIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTciIGxvY2F0b3I9IjIyOjEtMjI6MTIwIiBuYW1lPSJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTgiIGxvY2F0b3I9IjIzOjEtMjM6OTQiIG5hbWU9IkVEIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RUQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE5IiBsb2NhdG9yPSIyNDoxLTI0OjEwMCIgbmFtZT0iRW5jb3VudGVyIElucGF0aWVudCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjY2Ni41LjMwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtFbmNvdW50ZXIgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjAiIGxvY2F0b3I9IjI1OjEtMjU6MTIyIiBuYW1lPSJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjAiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIxIiBsb2NhdG9yPSIyNjoxLTI2Ojk5IiBuYW1lPSJGSVQgRE5BIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RklUIEROQSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjIiIGxvY2F0b3I9IjI3OjEtMjc6MTE0IiBuYW1lPSJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RmxleGlibGUgU2lnbW9pZG9zY29weSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjMiIGxvY2F0b3I9IjI4OjEtMjg6MTA2IiBuYW1lPSJGcmFpbHR5IERldmljZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExOC4xMi4xMzAwIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMyI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0ZyYWlsdHkgRGV2aWNlJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyNCIgbG9jYXRvcj0iMjk6MS0yOToxMDkiIG5hbWU9IkZyYWlsdHkgRGlhZ25vc2lzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RnJhaWx0eSBEaWFnbm9zaXMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI1IiBsb2NhdG9yPSIzMDoxLTMwOjEwOSIgbmFtZT0iRnJhaWx0eSBFbmNvdW50ZXIiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4OCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjUiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IEVuY291bnRlciZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg4JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjYiIGxvY2F0b3I9IjMxOjEtMzE6MTA3IiBuYW1lPSJGcmFpbHR5IFN5bXB0b20iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IFN5bXB0b20mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI3IiBsb2NhdG9yPSIzMjoxLTMyOjExNiIgbmFtZT0iSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7SG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyOCIgbG9jYXRvcj0iMzM6MS0zMzoxMDQiIG5hbWU9Ikhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyOCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0hvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjkiIGxvY2F0b3I9IjM0OjEtMzQ6MTE5IiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMwIiBsb2NhdG9yPSIzNToxLTM1OjExMCIgbmFtZT0iTm9uYWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMwIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Tm9uYWN1dGUgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMSIgbG9jYXRvcj0iMzY6MS0zNjoxMTQiIG5hbWU9Ik51cnNpbmcgRmFjaWxpdHkgVmlzaXQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtOdXJzaW5nIEZhY2lsaXR5IFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTInPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMiIgbG9jYXRvcj0iMzc6MS0zNzoxMDMiIG5hbWU9Ik9ic2VydmF0aW9uIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2JzZXJ2YXRpb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Nic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMzIiBsb2NhdG9yPSIzODoxLTM4OjEwNCIgbmFtZT0iT2ZmaWNlIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2ZmaWNlIFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNCIgbG9jYXRvcj0iMzk6MS0zOToxMDIiIG5hbWU9Ik91dHBhdGllbnQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzQiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtPdXRwYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODcnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNSIgbG9jYXRvcj0iNDA6MS00MDoxNTQiIG5hbWU9IlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjM1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7UHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyNSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjM2IiBsb2NhdG9yPSI0MToxLTQxOjE0OCIgbmFtZT0iUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzLUluaXRpYWwgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyMyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzciIGxvY2F0b3I9IjQyOjEtNDI6MTA3IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzciPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtUb3RhbCBDb2xlY3RvbXkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICA8L3ZhbHVlU2V0cz4NCiAgIDxjb2Rlcz4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzkiIGxvY2F0b3I9IjQ0OjEtNDQ6NjIiIG5hbWU9IkJpcnRoIGRhdGUiIGlkPSIyMTExMi04IiBkaXNwbGF5PSJCaXJ0aCBkYXRlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIzOSI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGUgJnF1b3Q7QmlydGggZGF0ZSZxdW90OzogJzIxMTEyLTgnIGZyb20gPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjM4Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TE9JTkMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gZGlzcGxheSAnQmlydGggZGF0ZSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYWxJZD0iMzgiIGxvY2F0b3I9IjQ0OjM1LTQ0OjQxIiBuYW1lPSJMT0lOQyIvPg0KICAgICAgPC9kZWY+DQogICA8L2NvZGVzPg0KICAgPHN0YXRlbWVudHM+DQogICAgICA8ZGVmIGxvY2F0b3I9IjQ5OjEtNDk6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDk6MS00OToxNSIgZGF0YVR5cGU9ImZoaXI6UGF0aWVudCIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiIHhzaTp0eXBlPSJSZXRyaWV2ZSIvPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjQ4IiBsb2NhdG9yPSI1MToxLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0OCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgRXRobmljaXR5JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBFdGhuaWNpdHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ3IiBsb2NhdG9yPSI1MjozLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjUxIiBsb2NhdG9yPSI1NDoxLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjUxIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O1NERSBQYXllciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUGF5ZXImcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUwIiBsb2NhdG9yPSI1NTozLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGxpYnJhcnlOYW1lPSJTREUiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNTQiIGxvY2F0b3I9IjU3OjEtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgUmFjZSZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUmFjZSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTMiIGxvY2F0b3I9IjU4OjMtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjU3IiBsb2NhdG9yPSI2MDoxLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NyI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgU2V4JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjU1Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBTZXgmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjU2IiBsb2NhdG9yPSI2MTozLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjY4IiBsb2NhdG9yPSI2MzoxLTY1OjYyIiBuYW1lPSJJbml0aWFsIFBvcHVsYXRpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjY4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0luaXRpYWwgUG9wdWxhdGlvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjciPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI2MyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWdlSW5ZZWFyc0F0KDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXJ0IG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYxIj4gYmV0d2VlbiA1MCBhbmQgNzU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KCSBhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjY2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtRdWFsaWZ5aW5nIEVuY291bnRlcnMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2NyIgbG9jYXRvcj0iNjQ6My02NTo2MiIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MyIgbG9jYXRvcj0iNjQ6My02NDo2MyIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iR3JlYXRlck9yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MSIgbG9jYXRvcj0iNjQ6NTUtNjQ6NTYiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNTAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iTGVzc09yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MiIgbG9jYXRvcj0iNjQ6NjItNjQ6NjMiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNzUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NiIgbG9jYXRvcj0iNjU6Ny02NTo2MiIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NSIgbG9jYXRvcj0iNjU6MTQtNjU6NjIiIG5hbWU9IlF1YWxpZnlpbmcgRW5jb3VudGVycyIgbGlicmFyeU5hbWU9IkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNzAiIGxvY2F0b3I9IjY3OjEtNjg6MjIiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3MCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtEZW5vbWluYXRvciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjkiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJbml0aWFsIFBvcHVsYXRpb24mcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2OSIgbG9jYXRvcj0iNjg6My02ODoyMiIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9Ijg3IiBsb2NhdG9yPSI3MDoxLTczOjE0OSIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9Ijg3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI4NiI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjcxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmxleGlibGVTaWdtb2lkb3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZsZXhpYmxlU2lnbW9pZG9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkdsb2JhbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GbGV4aWJsZVNpZ21vaWRvc2NvcHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz41IHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iODYiIGxvY2F0b3I9IjcxOjMtNzM6MTQ5IiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI3MiIgbG9jYXRvcj0iNzE6My03MTo2MSIgYWxpYXM9IkZsZXhpYmxlU2lnbW9pZG9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI3MSIgbG9jYXRvcj0iNzE6My03MTozOSIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI3MToxNS03MTozOCIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI4NSIgbG9jYXRvcj0iNzI6MjMtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijc2IiBsb2NhdG9yPSI3MjoyOS03Mjo3MCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NCIgbG9jYXRvcj0iNzI6MjktNzI6NTYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZsZXhpYmxlU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NSIgbG9jYXRvcj0iNzI6NjAtNzI6NzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4NCIgbG9jYXRvcj0iNzM6MjgtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjk0LTczOjEwOCIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjg5LTczOjkyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgwIiBsb2NhdG9yPSI3MzoyOC03Mzo4NyIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzkiIGxvY2F0b3I9IjczOjU2LTczOjg2IiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjczOjEyMy03MzoxNDkiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MyIgbG9jYXRvcj0iNzM6OTQtNzM6MTAwIiB2YWx1ZT0iNSIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3Mzo5NC03MzoxMDgiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgyIiBsb2NhdG9yPSI3MzoxMjMtNzM6MTQ5IiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgxIiBsb2NhdG9yPSI3MzoxMzAtNzM6MTQ5IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTA0IiBsb2NhdG9yPSI3NToxLTc4OjE0MCIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTA0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0NUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjEwMyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4OSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NUIENvbG9ub2dyYXBoeSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwMiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Db2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub2dyYXBoeTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+NSB5ZWFyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBvciBsZXNzIG9uIG9yIGJlZm9yZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEwMyIgbG9jYXRvcj0iNzY6My03ODoxNDAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijg5IiBsb2NhdG9yPSI3NjozLTc2OjQ1IiBhbGlhcz0iQ29sb25vZ3JhcGh5Ij4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9Ijg4IiBsb2NhdG9yPSI3NjozLTc2OjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9Ijc2OjE1LTc2OjMxIiBuYW1lPSJDVCBDb2xvbm9ncmFwaHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTAyIiBsb2NhdG9yPSI3NzoyMy03ODoxNDAiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTMiIGxvY2F0b3I9Ijc3OjI5LTc3OjYxIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkxIiBsb2NhdG9yPSI3NzoyOS03Nzo0NyIgcGF0aD0ic3RhdHVzIiBzY29wZT0iQ29sb25vZ3JhcGh5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkyIiBsb2NhdG9yPSI3Nzo1MS03Nzo2MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwMSIgbG9jYXRvcj0iNzg6MjgtNzg6MTQwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc4Ojg1LTc4Ojk5IiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODAtNzg6ODMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTciIGxvY2F0b3I9Ijc4OjI4LTc4Ojc4IiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5NiIgbG9jYXRvcj0iNzg6NTYtNzg6NzciIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IkNvbG9ub2dyYXBoeSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3ODo4NS03ODo5OSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI3ODoxMTQtNzg6MTQwIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTAwIiBsb2NhdG9yPSI3ODo4NS03ODo5MSIgdmFsdWU9IjUiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMjAiIGxvY2F0b3I9IjgwOjEtODM6MTIzIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMjAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTE5Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215JnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExOCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTciPiBzdGFydHMgb24gb3IgYmVmb3JlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxMTkiIGxvY2F0b3I9IjgxOjMtODM6MTIzIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMDYiIGxvY2F0b3I9IjgxOjMtODE6NDIiIGFsaWFzPSJDb2xlY3RvbXkiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTA1IiBsb2NhdG9yPSI4MTozLTgxOjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9IjgxOjE1LTgxOjMxIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTE4IiBsb2NhdG9yPSI4MjoyMy04MzoxMjMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEwIiBsb2NhdG9yPSI4MjoyOS04Mjo1OCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMDgiIGxvY2F0b3I9IjgyOjI5LTgyOjQ0IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJDb2xlY3RvbXkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTA5IiBsb2NhdG9yPSI4Mjo0OC04Mjo1OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNyIgbG9jYXRvcj0iODM6MjgtODM6MTIzIiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjc3LTgzOjgyIiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTE0IiBsb2NhdG9yPSI4MzoyOC04Mzo3NSIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEzIiBsb2NhdG9yPSI4Mzo1Ni04Mzo3NCIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sZWN0b215IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNiIgbG9jYXRvcj0iODM6OTctODM6MTIzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNSIgbG9jYXRvcj0iODM6MTA0LTgzOjEyMyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0MyIgbG9jYXRvcj0iODU6MS04OToxMTgiIG5hbWU9IkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTQzIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTQyIj4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltPYnNlcnZhdGlvbjogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GZWNhbE9jY3VsdFJlc3VsdDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaW4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPns8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2ZpbmFsJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhbWVuZGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb3JyZWN0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J3ByZWxpbWluYXJ5JzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPn08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbm90IG51bGw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZlY2FsT2NjdWx0UmVzdWx0PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+IGR1cmluZyBkYXkgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTQyIiBsb2NhdG9yPSI4NjozLTg5OjExOCIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTIyIiBsb2NhdG9yPSI4NjozLTg2OjY3IiBhbGlhcz0iRmVjYWxPY2N1bHRSZXN1bHQiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTIxIiBsb2NhdG9yPSI4NjozLTg2OjQ5IiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI4NjoxNy04Njo0OCIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE0MSIgbG9jYXRvcj0iODc6MjMtODk6MTE4IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzNCIgbG9jYXRvcj0iODc6MjktODg6NjIiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMwIiBsb2NhdG9yPSI4NzoyOS04NzoxMDQiIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI0IiBsb2NhdG9yPSI4NzoyOS04Nzo1MiIgcGF0aD0ic3RhdHVzIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI5IiBsb2NhdG9yPSI4Nzo1Ny04NzoxMDQiIHhzaTp0eXBlPSJMaXN0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNSIgbG9jYXRvcj0iODc6NTgtODc6NjQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNiIgbG9jYXRvcj0iODc6NjctODc6NzUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJhbWVuZGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iMTI3IiBsb2NhdG9yPSI4Nzo3OC04Nzo4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvcnJlY3RlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyOCIgbG9jYXRvcj0iODc6OTEtODc6MTAzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzMiIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzMiIgbG9jYXRvcj0iODg6MjgtODg6NTAiIHBhdGg9InZhbHVlIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTQwIiBsb2NhdG9yPSI4OToyOC04OToxMTgiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iSW5jbHVkZWRJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzgiIGxvY2F0b3I9Ijg5OjI4LTg5OjgzIiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzciIGxvY2F0b3I9Ijg5OjU2LTg5OjgyIiBwYXRoPSJlZmZlY3RpdmUiIHNjb3BlPSJGZWNhbE9jY3VsdFJlc3VsdCIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzkiIGxvY2F0b3I9Ijg5Ojk5LTg5OjExOCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2OCIgbG9jYXRvcj0iOTE6MS05NToxNDMiIG5hbWU9IkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxNjgiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7RmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjE2NyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7RklUIEROQSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBGaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGluIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz57PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidmaW5hbCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYW1lbmRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29ycmVjdGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidwcmVsaW1pbmFyeSc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz59PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZpdEROQTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyBub3QgbnVsbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5HbG9iYWw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Rml0RE5BPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+b2NjdXJzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTY3IiBsb2NhdG9yPSI5MjozLTk1OjE0MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTQ1IiBsb2NhdG9yPSI5MjozLTkyOjMzIiBhbGlhcz0iRml0RE5BIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE0NCIgbG9jYXRvcj0iOTI6My05MjoyNiIgZGF0YVR5cGU9ImZoaXI6T2JzZXJ2YXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9PYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTI6MTctOTI6MjUiIG5hbWU9IkZJVCBETkEiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTY2IiBsb2NhdG9yPSI5MzoyMy05NToxNDMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTU3IiBsb2NhdG9yPSI5MzoyOS05NDo1MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTMiIGxvY2F0b3I9IjkzOjI5LTkzOjkzIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0NyIgbG9jYXRvcj0iOTM6MjktOTM6NDEiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTIiIGxvY2F0b3I9IjkzOjQ2LTkzOjkzIiB4c2k6dHlwZT0iTGlzdCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDgiIGxvY2F0b3I9IjkzOjQ3LTkzOjUzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDkiIGxvY2F0b3I9IjkzOjU2LTkzOjY0IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYW1lbmRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjE1MCIgbG9jYXRvcj0iOTM6NjctOTM6NzciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb3JyZWN0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNTEiIGxvY2F0b3I9IjkzOjgwLTkzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTYiIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE1NSIgbG9jYXRvcj0iOTQ6MjgtOTQ6MzkiIHBhdGg9InZhbHVlIiBzY29wZT0iRml0RE5BIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2NSIgbG9jYXRvcj0iOTU6MjgtOTU6MTQzIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjM1LTk1OjcyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MSIgbG9jYXRvcj0iOTU6MjgtOTU6NzIiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MCIgbG9jYXRvcj0iOTU6NTYtOTU6NzEiIHBhdGg9ImVmZmVjdGl2ZSIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NTo4MS05NTo5NSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYzIiBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MiIgbG9jYXRvcj0iOTU6MTI0LTk1OjE0MyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjQiIGxvY2F0b3I9Ijk1OjgxLTk1Ojg3IiB2YWx1ZT0iMyIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIxNjMiIGxvY2F0b3I9Ijk1OjExNy05NToxNDMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYyIiBsb2NhdG9yPSI5NToxMjQtOTU6MTQzIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MyIgbG9jYXRvcj0iOTU6MTE3LTk1OjE0MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjIiIGxvY2F0b3I9Ijk1OjEyNC05NToxNDMiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxODUiIGxvY2F0b3I9Ijk3OjEtMTAwOjE0MSIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxODUiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7Q29sb25vc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxODQiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTcwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtDb2xvbm9zY29weSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkgIGFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MTAgeWVhcnM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gb3IgbGVzcyBvbiBvciBiZWZvcmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE4NCIgbG9jYXRvcj0iOTg6My0xMDA6MTQxIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxNzAiIGxvY2F0b3I9Ijk4OjMtOTg6NDAiIGFsaWFzPSJDb2xvbm9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxNjkiIGxvY2F0b3I9Ijk4OjMtOTg6MjgiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTg6MTUtOTg6MjciIG5hbWU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE4MyIgbG9jYXRvcj0iOTk6MjMtMTAwOjE0MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzQiIGxvY2F0b3I9Ijk5OjI5LTk5OjYwIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MiIgbG9jYXRvcj0iOTk6MjktOTk6NDYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MyIgbG9jYXRvcj0iOTk6NTAtOTk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxODIiIGxvY2F0b3I9IjEwMDoyOS0xMDA6MTQxIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEwMDo4NS0xMDA6MTAwIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjgwLTEwMDo4MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzgiIGxvY2F0b3I9IjEwMDoyOS0xMDA6NzgiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3NyIgbG9jYXRvcj0iMTAwOjU3LTEwMDo3NyIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sb25vc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTAwOjExNS0xMDA6MTQxIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgxIiBsb2NhdG9yPSIxMDA6ODUtMTAwOjkyIiB2YWx1ZT0iMTAiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyMDAiIGxvY2F0b3I9IjEwMjoxLTEwNzo1OSIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMDAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7TnVtZXJhdG9yJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxOTkiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTYiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NvbG9ub3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTkyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5OCI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7Q1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxOTkiIGxvY2F0b3I9IjEwMzozLTEwNzo1OSIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5NiIgbG9jYXRvcj0iMTAzOjMtMTA2OjYzIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkzIiBsb2NhdG9yPSIxMDM6My0xMDU6NjYiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTAiIGxvY2F0b3I9IjEwMzozLTEwNDo2NyIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NyIgbG9jYXRvcj0iMTAzOjMtMTAzOjMyIiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NiIgbG9jYXRvcj0iMTAzOjEwLTEwMzozMiIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg5IiBsb2NhdG9yPSIxMDQ6MjYtMTA0OjY3IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4OCIgbG9jYXRvcj0iMTA0OjMzLTEwNDo2NyIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkyIiBsb2NhdG9yPSIxMDU6MjYtMTA1OjY2IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5MSIgbG9jYXRvcj0iMTA1OjMzLTEwNTo2NiIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTUiIGxvY2F0b3I9IjEwNjoyNi0xMDY6NjMiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTk0IiBsb2NhdG9yPSIxMDY6MzMtMTA2OjYzIiBuYW1lPSJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5OCIgbG9jYXRvcj0iMTA3OjI2LTEwNzo1OSIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTciIGxvY2F0b3I9IjEwNzozMy0xMDc6NTkiIG5hbWU9IkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjEwIiBsb2NhdG9yPSIxMDk6MS0xMTE6MTIwIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxMCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20mcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjIwOSI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bQ29uZGl0aW9uOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01hbGlnbmFudCBOZW9wbGFzbSBvZiBDb2xvbiZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvcmVjdGFsQ2FuY2VyPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDgiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtQcmV2YWxlbmNlIFBlcmlvZCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q29sb3JlY3RhbENhbmNlcjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwOCI+IHN0YXJ0cyBvbiBvciBiZWZvcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDkiIGxvY2F0b3I9IjExMDozLTExMToxMjAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjIwMiIgbG9jYXRvcj0iMTEwOjMtMTEwOjYxIiBhbGlhcz0iQ29sb3JlY3RhbENhbmNlciI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDEiIGxvY2F0b3I9IjExMDozLTExMDo0NCIgZGF0YVR5cGU9ImZoaXI6Q29uZGl0aW9uIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSIxMTA6MTUtMTEwOjQzIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMjA4IiBsb2NhdG9yPSIxMTE6MjMtMTExOjEyMCIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTE6NzQtMTExOjc5IiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjA1IiBsb2NhdG9yPSIxMTE6MjktMTExOjcyIiBuYW1lPSJQcmV2YWxlbmNlIFBlcmlvZCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNCIgbG9jYXRvcj0iMTExOjU2LTExMTo3MSIgbmFtZT0iQ29sb3JlY3RhbENhbmNlciIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMDciIGxvY2F0b3I9IjExMTo5NC0xMTE6MTIwIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNiIgbG9jYXRvcj0iMTExOjEwMS0xMTE6MTIwIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjI1IiBsb2NhdG9yPSIxMTM6MS0xMTg6OTAiIG5hbWU9IkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0Rlbm9taW5hdG9yIEV4Y2x1c2lvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMjI0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+SG9zcGljZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGFzIEhvc3BpY2UmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWFsaWduYW50IE5lb3BsYXNtJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GcmFpbHR5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7QWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZyYWlsdHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0xvbmcgVGVybSBDYXJlIFBlcmlvZHMgTG9uZ2VyIFRoYW4gOTAgQ29uc2VjdXRpdmUgRGF5cyZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjIyNCIgbG9jYXRvcj0iMTE0OjMtMTE4OjkwIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE4IiBsb2NhdG9yPSIxMTQ6My0xMTY6NTkiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTUiIGxvY2F0b3I9IjExNDozLTExNTo1MiIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIxMiIgbG9jYXRvcj0iMTE0OjMtMTE0OjIzIiBuYW1lPSJIYXMgSG9zcGljZSIgbGlicmFyeU5hbWU9Ikhvc3BpY2UiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTQiIGxvY2F0b3I9IjExNToyNi0xMTU6NTIiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjEzIiBsb2NhdG9yPSIxMTU6MzMtMTE1OjUyIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTciIGxvY2F0b3I9IjExNjoyNi0xMTY6NTkiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE2IiBsb2NhdG9yPSIxMTY6MzMtMTE2OjU5IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIzIiBsb2NhdG9yPSIxMTc6MjYtMTE4OjkwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyMCIgbG9jYXRvcj0iMTE3OjI2LTExNzoxMDAiIG5hbWU9IkFkdmFuY2VkIElsbG5lc3MgYW5kIEZyYWlsdHkgRXhjbHVzaW9uIE5vdCBJbmNsdWRpbmcgT3ZlciBBZ2UgODAiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIyIiBsb2NhdG9yPSIxMTg6MjctMTE4OjkwIiBuYW1lPSJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICA8L3N0YXRlbWVudHM+DQo8L2xpYnJhcnk+DQo=" + }, + { + "contentType": "application/elm+json", + "data": "ew0KICAgImxpYnJhcnkiIDogew0KICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjEuMyIsDQogICAgICAgICAidHJhbnNsYXRvck9wdGlvbnMiIDogIkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iLA0KICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyINCiAgICAgIH0sIHsNCiAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICJyIiA6ICIyMjUiLA0KICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwibGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMSciIF0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfQ0KICAgICAgfSBdLA0KICAgICAgImlkZW50aWZpZXIiIDogew0KICAgICAgICAgImlkIiA6ICJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiLA0KICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiwNCiAgICAgICAgICJ2ZXJzaW9uIiA6ICIwLjAuMDAxIg0KICAgICAgfSwNCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsNCiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwNCiAgICAgICAgICJ2ZXJzaW9uIiA6ICJyMSINCiAgICAgIH0sDQogICAgICAidXNpbmdzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLA0KICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIg0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM6MS0zOjI2IiwNCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVIiLA0KICAgICAgICAgICAgInVyaSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpciIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ1c2luZyAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZISVIiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIic0LjAuMSciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAiaW5jbHVkZXMiIDogew0KICAgICAgICAgImRlZiIgOiBbIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OjEtNTo1NiIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0ZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMDAxIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZISVJIZWxwZXJzIiBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgdmVyc2lvbiAiLCInNC4wLjAwMSciLCIgY2FsbGVkICIsIkZISVJIZWxwZXJzIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6MS02OjY2IiwNCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIlNERSIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL1N1cHBsZW1lbnRhbERhdGFFbGVtZW50c0ZISVI0IiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICIyLjAuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlN1cHBsZW1lbnRhbERhdGFFbGVtZW50c0ZISVI0IiBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgdmVyc2lvbiAiLCInMi4wLjAwMCciLCIgY2FsbGVkICIsIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI0IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OjEtNzo2OSIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgInBhdGgiIDogImh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiNi4wLjAwMCIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImluY2x1ZGUgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIHZlcnNpb24gIiwiJzYuMC4wMDAnIiwiIGNhbGxlZCAiLCJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiODoxLTg6ODkiLA0KICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycyIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiMi4wLjAwMCIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImluY2x1ZGUgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzRkhJUjQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIicyLjAuMDAwJyIsIiBjYWxsZWQgIiwiQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI2IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OjEtOTo1MyIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJIb3NwaWNlIiwNCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvSG9zcGljZUZISVI0IiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICIyLjAuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkhvc3BpY2VGSElSNCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIHZlcnNpb24gIiwiJzIuMC4wMDAnIiwiIGNhbGxlZCAiLCJIb3NwaWNlIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjciLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6ODUiLA0KICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRnJhaWx0eSIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiLA0KICAgICAgICAgICAgInZlcnNpb24iIDogIjUuMTIuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIic1LjEyLjAwMCciLCIgY2FsbGVkICIsIkZyYWlsdHkiIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAicGFyYW1ldGVycyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjQ1IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NjoxLTQ3OjY2IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiNDUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwicGFyYW1ldGVyICIsIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiLCIgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDQiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiSW50ZXJ2YWw8IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRGF0ZVRpbWUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIj4iIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICBkZWZhdWx0ICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQyIiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJJbnRlcnZhbFsiLCJAMjAxOS0wMS0wMVQwMDowMDowMC4wIiwiLCAiLCJAMjAyMC0wMS0wMVQwMDowMDowMC4wIiwiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJkZWZhdWx0IiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0MiIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTEtNDc6NjYiLA0KICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgImxvdyIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNDAiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ3OjIwLTQ3OjQxIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsDQogICAgICAgICAgICAgICAgICAieWVhciIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDE5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJkYXkiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtaW51dGUiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAic2Vjb25kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjQxIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo0NC00Nzo2NSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgInllYXIiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAibW9udGgiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAiZGF5IiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgImhvdXIiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgInNlY29uZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtaWxsaXNlY29uZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICJwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0NCIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6MzItNDY6NDkiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsDQogICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0MyIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6NDEtNDY6NDgiLA0KICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciINCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJjb2RlU3lzdGVtcyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MzgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkxPSU5DIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2xvaW5jLm9yZyIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImNvZGVzeXN0ZW0gIiwiXCJMT0lOQ1wiIiwiOiAiLCInaHR0cDovL2xvaW5jLm9yZyciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAidmFsdWVTZXRzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiOSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDoxMDciLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFjdXRlIElucGF0aWVudCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDgzIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJBY3V0ZSBJbnBhdGllbnRcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDgzJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6MS0xNToxMDgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFkdmFuY2VkIElsbG5lc3MiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4MiIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkFkdmFuY2VkIElsbG5lc3NcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY6MS0xNjoxMDMiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFubnVhbCBXZWxsbmVzcyBWaXNpdCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjEyNDAiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTEiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJBbm51YWwgV2VsbG5lc3MgVmlzaXRcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjEyNDAnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTE3OjEzOSIsDQogICAgICAgICAgICAibmFtZSIgOiAiQ2FyZSBTZXJ2aWNlcyBpbiBMb25nLVRlcm0gUmVzaWRlbnRpYWwgRmFjaWxpdHkiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMiIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTAzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjEzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiQ29sb25vc2NvcHlcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk6MS0xOToxMDciLA0KICAgICAgICAgICAgIm5hbWUiIDogIkNUIENvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM4IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiQ1QgQ29sb25vZ3JhcGh5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjEtMjA6MTEyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5Ni4xMi4xNTEwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRGVtZW50aWEgTWVkaWNhdGlvbnNcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5Ni4xMi4xNTEwJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNiIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MS0yMToxMzYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkRpc2NoYXJnZWQgdG8gSGVhbHRoIENhcmUgRmFjaWxpdHkgZm9yIEhvc3BpY2UgQ2FyZSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmVcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDcnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTIyOjEyMCIsDQogICAgICAgICAgICAibmFtZSIgOiAiRGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE3IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmVcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDknIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzoxLTIzOjk0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJFRCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRURcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MS0yNDoxMDAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkVuY291bnRlciBJbnBhdGllbnQiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy42NjYuNS4zMDciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJFbmNvdW50ZXIgSW5wYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy42NjYuNS4zMDcnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNToxLTI1OjEyMiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTEiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVClcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDExJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MS0yNjo5OSIsDQogICAgICAgICAgICAibmFtZSIgOiAiRklUIEROQSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIxIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRklUIEROQVwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzknIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoxLTI3OjExNCIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIyIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRmxleGlibGUgU2lnbW9pZG9zY29weVwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIzIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODoxLTI4OjEwNiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRnJhaWx0eSBEZXZpY2UiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTguMTIuMTMwMCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMyIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkZyYWlsdHkgRGV2aWNlXCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTguMTIuMTMwMCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjEtMjk6MTA5IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJGcmFpbHR5IERpYWdub3NpcyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc0IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjI0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRnJhaWx0eSBEaWFnbm9zaXNcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc0JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyNSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zMDoxMDkiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkZyYWlsdHkgRW5jb3VudGVyIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGcmFpbHR5IEVuY291bnRlclwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjI2IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToxLTMxOjEwNyIsDQogICAgICAgICAgICAibmFtZSIgOiAiRnJhaWx0eSBTeW1wdG9tIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzUiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGcmFpbHR5IFN5bXB0b21cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyNyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MS0zMjoxMTYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE2IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjI3IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzXCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNiciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjEtMzM6MTA0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMTEwOC4xNSIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyOCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjI5IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDoxLTM0OjExOSIsDQogICAgICAgICAgICAibmFtZSIgOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMDEiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb25cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzMCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS0zNToxMTAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk5vbmFjdXRlIElucGF0aWVudCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg0IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMwIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiTm9uYWN1dGUgSW5wYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjEtMzY6MTE0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTIiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzEiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzIiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjEtMzc6MTAzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJPYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMyIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiT2JzZXJ2YXRpb25cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzMyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6MS0zODoxMDQiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk9mZmljZSBWaXNpdCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDAxIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiT2ZmaWNlIFZpc2l0XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMSciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjEtMzk6MTAyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJPdXRwYXRpZW50IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzQiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJPdXRwYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzUiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjEtNDA6MTU0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmV2ZW50aXZlIENhcmUgU2VydmljZXMgLSBFc3RhYmxpc2hlZCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjM1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXBcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzNiIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MS00MToxNDgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjMiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcFwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjMnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjM3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxLTQyOjEwNyIsDQogICAgICAgICAgICAibmFtZSIgOiAiVG90YWwgQ29sZWN0b215IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTkiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJUb3RhbCBDb2xlY3RvbXlcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDE5JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJjb2RlcyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjM5IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NDoxLTQ0OjYyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJCaXJ0aCBkYXRlIiwNCiAgICAgICAgICAgICJpZCIgOiAiMjExMTItOCIsDQogICAgICAgICAgICAiZGlzcGxheSIgOiAiQmlydGggZGF0ZSIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIzOSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJjb2RlICIsIlwiQmlydGggZGF0ZVwiIiwiOiAiLCInMjExMTItOCciLCIgZnJvbSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIzOCIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkxPSU5DXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBkaXNwbGF5ICIsIidCaXJ0aCBkYXRlJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMzgiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ0OjM1LTQ0OjQxIiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJMT0lOQyINCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS00OToxNSIsDQogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsDQogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS00OToxNSIsDQogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLA0KICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSINCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI0OCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MS01MjoyMSIsDQogICAgICAgICAgICAibmFtZSIgOiAiU0RFIEV0aG5pY2l0eSIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI0OCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgRXRobmljaXR5XCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIEV0aG5pY2l0eVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNDciLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjMtNTI6MjEiLA0KICAgICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNTEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEtNTU6MTciLA0KICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1MSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgUGF5ZXJcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNTAiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiU0RFIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJTREUgUGF5ZXJcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjUwIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NTozLTU1OjE3IiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUGF5ZXIiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNTQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjEtNTg6MTYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIlNERSBSYWNlIiwNCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjU0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIlNERSBSYWNlXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUzIiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIFJhY2VcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjUzIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1ODozLTU4OjE2IiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsDQogICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIlNERSIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI1NyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6MS02MToxNSIsDQogICAgICAgICAgICAibmFtZSIgOiAiU0RFIFNleCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1NyIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgU2V4XCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIFNleFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTYiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjMtNjE6MTUiLA0KICAgICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjYzOjEtNjU6NjIiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkluaXRpYWwgUG9wdWxhdGlvbiIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI2OCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJJbml0aWFsIFBvcHVsYXRpb25cIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjciLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQWdlSW5ZZWFyc0F0IiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXJ0IG9mICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBiZXR3ZWVuICIsIjUwIiwiIGFuZCAiLCI3NSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG5cdCBhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjY1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjY0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI2NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIlF1YWxpZnlpbmcgRW5jb3VudGVyc1wiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjY3IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDozLTY1OjYyIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNjMiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjMtNjQ6NjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJHcmVhdGVyT3JFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6My02NDo0NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicHJlY2lzaW9uIiA6ICJZZWFyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYWxjdWxhdGVBZ2VBdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUudmFsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjE2LTY0OjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjI1LTY0OjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjYxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDo1NS02NDo1NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICI1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxlc3NPckVxdWFsIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjYwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDozLTY0OjQ1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJwcmVjaXNpb24iIDogIlllYXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNhbGN1bGF0ZUFnZUF0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlRvRGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZS52YWx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI1OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MTYtNjQ6NDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MjUtNjQ6NDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjYyLTY0OjYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjc1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2NiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6Ny02NTo2MiIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhpc3RzIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2NSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MTQtNjU6NjIiLA0KICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlF1YWxpZnlpbmcgRW5jb3VudGVycyIsDQogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI3MCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNjc6MS02ODoyMiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRGVub21pbmF0b3IiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiNzAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRGVub21pbmF0b3JcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjkiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJJbml0aWFsIFBvcHVsYXRpb25cIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2OSIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjg6My02ODoyMiIsDQogICAgICAgICAgICAgICAibmFtZSIgOiAiSW5pdGlhbCBQb3B1bGF0aW9uIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgfQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjg3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MDoxLTczOjE0OSIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiODciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWRcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiODYiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiRmxleGlibGUgU2lnbW9pZG9zY29weVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJdIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJzdGF0dXMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsIj0iLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvbXBsZXRlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk5vcm1hbGl6ZSBJbnRlcnZhbFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZsZXhpYmxlU2lnbW9pZG9zY29weSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmRzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIjUgIiwieWVhcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBvciBsZXNzIG9uIG9yIGJlZm9yZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk1lYXN1cmVtZW50IFBlcmlvZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4NiIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6My03MzoxNDkiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwNCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzIiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcxOjMtNzE6NjEiLA0KICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjcxIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MTozLTcxOjM5IiwNCiAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvY2VkdXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0ZW1wbGF0ZUlkIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MToxNS03MTozOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODUiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjIzLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjc2IiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoyOS03Mjo3MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjI5LTcyOjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGF0dXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjYwLTcyOjcwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiY29tcGxldGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODQiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjI4LTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjk0LTczOjEwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6ODktNzM6OTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjI4LTczOjg3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo1Ni03Mzo4NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRmxleGlibGVTaWdtb2lkb3Njb3B5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjk0LTczOjEwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjEyMy03MzoxNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN1YnRyYWN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMjMtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMzAtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6OTQtNzM6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiA1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MTIzLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MTMwLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo5NC03MzoxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6OTQtNzM6MTA4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMjMtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMzAtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTA0IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToxLTc4OjE0MCIsDQogICAgICAgICAgICAibmFtZSIgOiAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDQiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDMiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ1QgQ29sb25vZ3JhcGh5XCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIl0iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsIkNvbG9ub2dyYXBoeSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sb25vZ3JhcGh5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAic3RhdHVzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCI9IiwiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidjb21wbGV0ZWQnIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0XHRhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkdsb2JhbCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTm9ybWFsaXplIEludGVydmFsXCIiLCIoIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sb25vZ3JhcGh5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAicGVyZm9ybWVkIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIikiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmRzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICI1ICIsInllYXJzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgb3IgbGVzcyBvbiBvciBiZWZvcmUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI5OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTAzIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NjozLTc4OjE0MCIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4OSIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzY6My03Njo0NSIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODgiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjMtNzY6MzIiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVQcm9wZXJ0eSIgOiAiY29kZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZXMiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjE1LTc2OjMxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDVCBDb2xvbm9ncmFwaHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sDQogICAgICAgICAgICAgICAid2hlcmUiIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEwMiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6MjMtNzg6MTQwIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTMiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjI5LTc3OjYxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6MjktNzc6NDciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXR1cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6NTEtNzc6NjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb21wbGV0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDEiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjI4LTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4Ojg1LTc4Ojk5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo4MC03ODo4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MjgtNzg6NzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk5vcm1hbGl6ZSBJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjU2LTc4Ojc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJwZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xvbm9ncmFwaHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODoxMTQtNzg6MTQwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdWJ0cmFjdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTE0LTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTIxLTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo4NS03ODo5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjExNC03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjEyMS03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjExNC03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjEyMS03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMjAiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjEtODM6MTIzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjEyMCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExOSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJQcm9jZWR1cmUiLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJUb3RhbCBDb2xlY3RvbXlcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sZWN0b215IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIndoZXJlICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTA4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJDb2xlY3RvbXkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAic3RhdHVzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCI9IiwiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICInY29tcGxldGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdFx0YW5kICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTExIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkdsb2JhbCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk5vcm1hbGl6ZSBJbnRlcnZhbFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sZWN0b215IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTEzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsInN0YXJ0cyBvbiBvciBiZWZvcmUiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTE2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTE5IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTozLTgzOjEyMyIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDYiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjMtODE6NDIiLA0KICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJDb2xlY3RvbXkiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEwNSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6My04MTozMiIsDQogICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZVByb3BlcnR5IiA6ICJjb2RlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlQ29tcGFyYXRvciIgOiAiaW4iLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlcyIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6MTUtODE6MzEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvdGFsIENvbGVjdG9teSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTE4IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MjoyMy04MzoxMjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTAiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjI5LTgyOjU4IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjI5LTgyOjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGF0dXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xlY3RvbXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTA5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo0OC04Mjo1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImNvbXBsZXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MjgtODM6MTIzIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTYW1lT3JCZWZvcmUiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NzctODM6ODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI4LTgzOjc1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjExMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NTYtODM6NzQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInBlcmZvcm1lZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbGVjdG9teSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjk3LTgzOjEyMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjEwNC04MzoxMjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjg1OjEtODk6MTE4IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTQzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDIiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTIxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiT2JzZXJ2YXRpb24iLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVClcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiRmVjYWxPY2N1bHRSZXN1bHQiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAid2hlcmUgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGZWNhbE9jY3VsdFJlc3VsdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJzdGF0dXMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIGluICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ7IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidmaW5hbCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiwgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidhbWVuZGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMjciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvcnJlY3RlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiwgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidwcmVsaW1pbmFyeSciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIn0iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTMyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGZWNhbE9jY3VsdFJlc3VsdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ2YWx1ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaXMgbm90IG51bGwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZlY2FsT2NjdWx0UmVzdWx0IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVmZmVjdGl2ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsImR1cmluZyBkYXkgb2YiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDIiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMtODk6MTE4IiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsDQogICAgICAgICAgICAgICAic291cmNlIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyMiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6My04Njo2NyIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMjEiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMtODY6NDkiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxNy04Njo0OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLA0KICAgICAgICAgICAgICAgIndoZXJlIiA6IHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDEiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjIzLTg5OjExOCIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzNCIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MjktODg6NjIiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjI5LTg3OjEwNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzoyOS04Nzo1MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRmVjYWxPY2N1bHRSZXN1bHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo1Ny04NzoxMDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo1OC04Nzo2NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImZpbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NjctODc6NzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJhbWVuZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NzgtODc6ODgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb3JyZWN0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo5MS04NzoxMDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJwcmVsaW1pbmFyeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTMzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoyOC04ODo2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoyOC04ODo2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjI4LTg4OjUwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjgtODk6MTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICJwcmVjaXNpb24iIDogIkRheSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5jbHVkZWRJbiIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI4LTg5OjgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NTYtODk6ODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVmZmVjdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTktODk6MTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTY4IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxLTk1OjE0MyIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTY4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2NyIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJPYnNlcnZhdGlvbiIsIjogIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkZJVCBETkFcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiRml0RE5BIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIndoZXJlICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRml0RE5BIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXR1cyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaW4gIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInsiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2ZpbmFsJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2FtZW5kZWQnIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIsICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICInY29ycmVjdGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ3ByZWxpbWluYXJ5JyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAifSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdFx0YW5kICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTU0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZpdEROQSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ2YWx1ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaXMgbm90IG51bGwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTYxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZpdEROQSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlZmZlY3RpdmUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIm9jY3VycyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiMyAiLCJ5ZWFycyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTYyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NyIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6My05NToxNDMiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwNCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ1IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjozLTkyOjMzIiwNCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDQiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjMtOTI6MjYiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxNy05MjoyNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRklUIEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTY2IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyMy05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjI5LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyOS05Mzo5MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyOS05Mzo0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NDYtOTM6OTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo0Ny05Mzo1MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImZpbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NTYtOTM6NjQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJhbWVuZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NjctOTM6NzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb3JyZWN0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo4MC05Mzo5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInByZWxpbWluYXJ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjI4LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjI4LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MjgtOTQ6MzkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MjgtOTU6MTQzIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInByZWNpc2lvbiIgOiAiRGF5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTozNS05NTo3MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjI4LTk1OjcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6NTYtOTU6NzEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVmZmVjdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZpdEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTo4MS05NTo5NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjExNy05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN1YnRyYWN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTE3LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEyNC05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6ODciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTE3LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEyNC05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxMTctOTU6MTQzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTI0LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4NSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6MS0xMDA6MTQxIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTg1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkNvbG9ub3Njb3B5IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODQiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ29sb25vc2NvcHlcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sb25vc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAid2hlcmUgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTcxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkNvbG9ub3Njb3B5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXR1cyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiPSIsIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvbXBsZXRlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQgIGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE3NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkNvbG9ub3Njb3B5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZW5kcyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiMTAgIiwieWVhcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBvciBsZXNzIG9uIG9yIGJlZm9yZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4NCIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6My0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsDQogICAgICAgICAgICAgICAic291cmNlIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3MCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6My05ODo0MCIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9ub3Njb3B5IiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjkiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjMtOTg6MjgiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVQcm9wZXJ0eSIgOiAiY29kZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZXMiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjE1LTk4OjI3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTgzIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyMy0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc0IiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyOS05OTo2MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyOS05OTo0NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiQ29sb25vc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTczIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1MC05OTo2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImNvbXBsZXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MiIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjI5LTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6ODUtMTAwOjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjgwLTEwMDo4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDoyOS0xMDA6NzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk5vcm1hbGl6ZSBJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6NTctMTAwOjc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJwZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6ODUtMTAwOjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDoxMTUtMTAwOjE0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiU3VidHJhY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTgwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6MTE1LTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6MTIyLTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjg1LTEwMDo5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjExNS0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEyMi0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo4NS0xMDA6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo4NS0xMDA6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjExNS0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEyMi0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjAwIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6MS0xMDc6NTkiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk51bWVyYXRvciIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiTnVtZXJhdG9yXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5OSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTkzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE4NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkNvbG9ub3Njb3B5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTg5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImV4aXN0cyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWRcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImV4aXN0cyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdG9yICIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJleGlzdHMgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTkiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzozLTEwNzo1OSIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTk2IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6My0xMDY6NjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5MyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjMtMTA1OjY2IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzozLTEwNDo2NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6My0xMDM6MzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTAtMTAzOjMyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MjYtMTA0OjY3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeGlzdHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjMzLTEwNDo2NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjYtMTA1OjY2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeGlzdHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA1OjMzLTEwNTo2NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTUiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNjoyNi0xMDY6NjMiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTk0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MzMtMTA2OjYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5OCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjI2LTEwNzo1OSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhpc3RzIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzozMy0xMDc6NTkiLA0KICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTAiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOToxLTExMToxMjAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk1hbGlnbmFudCBOZW9wbGFzbSIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiTWFsaWduYW50IE5lb3BsYXNtXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJDb25kaXRpb24iLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb25cIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sb3JlY3RhbENhbmNlciIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJQcmV2YWxlbmNlIFBlcmlvZFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJDb2xvcmVjdGFsQ2FuY2VyIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsInN0YXJ0cyBvbiBvciBiZWZvcmUiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjA3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwOSIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMtMTExOjEyMCIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMDIiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDozLTExMDo2MSIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9yZWN0YWxDYW5jZXIiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwMSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMtMTEwOjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0ZW1wbGF0ZUlkIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6MTUtMTEwOjQzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sDQogICAgICAgICAgICAgICAid2hlcmUiIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjIzLTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlNhbWVPckJlZm9yZSIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NzQtMTExOjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MjktMTExOjcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQcmV2YWxlbmNlIFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NTYtMTExOjcxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvcmVjdGFsQ2FuY2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiINCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwNyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjk0LTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MTAxLTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjI1IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTM6MS0xMTg6OTAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMjUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRGVub21pbmF0b3IgRXhjbHVzaW9uXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyNCIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjE1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiSG9zcGljZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkhhcyBIb3NwaWNlXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk1hbGlnbmFudCBOZW9wbGFzbVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIlRvdGFsIENvbGVjdG9teSBQZXJmb3JtZWRcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRnJhaWx0eSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGcmFpbHR5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTG9uZyBUZXJtIENhcmUgUGVyaW9kcyBMb25nZXIgVGhhbiA5MCBDb25zZWN1dGl2ZSBEYXlzXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjI0IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6My0xMTg6OTAiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwNCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIxOCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjMtMTE2OjU5IiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTUiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozLTExNTo1MiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjEyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6My0xMTQ6MjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkhhcyBIb3NwaWNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiSG9zcGljZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyNi0xMTU6NTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjEzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTU6MzMtMTE1OjUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNYWxpZ25hbnQgTmVvcGxhc20iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjoyNi0xMTY6NTkiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjE2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6MzMtMTE2OjU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIzIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MjYtMTE4OjkwIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MjYtMTE3OjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiQWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCIsDQogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZyYWlsdHkiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MjctMTE4OjkwIiwNCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiLA0KICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGcmFpbHR5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfQ0KICAgfQ0KfQ==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ColorectalCancerScreeningsFHIR", + "ifNoneExist": "Library?url=http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR&version=0.0.002" + } + } + ]} diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json new file mode 100644 index 00000000000..81b2d0d7316 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json @@ -0,0 +1,3780 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle1", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.101.12.1001", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.113883.3.464.1003.101.12.1001" + } + ], + "version": "20180311", + "name": "OfficeVisit", + "title": "Office Visit", + "status": "active", + "experimental": false, + "publisher": "NLM", + "expansion": { + "identifier": "20200507", + "timestamp": "2021-01-14T20:59:46-07:00", + "contains": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", + "ifNoneExist": "ValueSet?url=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001&version=20180311" + } + } + ]} diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 0ba86977969..e378a7898cb 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index b12e654faf8..ed87536639b 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 8677b904bbe..9a89e84a27a 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-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 23a3f4ccfd9..409096bfece 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index e3d347cc12c..7f487527d73 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 1fb57ba2778..724c7982565 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 - 6.9.9-SNAPSHOT + 6.9.10-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 98b3407c903..72ee9e9f8c0 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index db74917e705..bc052942a09 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index b31d930227f..07b6c1ae492 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index eab80fc01a3..733d44e62ed 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 3c9b6e6c687..cb39d0a0092 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index d1336006d52..05784706690 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 82f33474782..6b9fc930787 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 - 6.9.9-SNAPSHOT + 6.9.10-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 7ed3464e804..dc1184e19b1 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 - 6.9.9-SNAPSHOT + 6.9.10-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 8ec6bcb529e..72435ed7ffa 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 - 6.9.9-SNAPSHOT + 6.9.10-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 961ea491248..5d9d9e68da6 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 - 6.9.9-SNAPSHOT + 6.9.10-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 1b4da7b170b..7f57f2984ce 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 - 6.9.9-SNAPSHOT + 6.9.10-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 f44873e9e6a..4ddb19ef8ef 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index d11066607d5..02a0a156d27 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 0ec080fa1e6..ce88ab99618 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 01786c9d945..e9487c22356 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 4c37982117a..e17a02189a2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. @@ -983,7 +983,7 @@ 1.0.8 - 3.0.0-PRE7 + 3.0.0-PRE8 5.4.1 diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 95bb4d038b7..632c575bedb 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 - 6.9.9-SNAPSHOT + 6.9.10-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 bd679c2e152..1348b36fe55 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 - 6.9.9-SNAPSHOT + 6.9.10-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 b5570d3c637..1f5d5ddf56a 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 - 6.9.9-SNAPSHOT + 6.9.10-SNAPSHOT ../../pom.xml From 90bfe05b9d25a76d8503578a5bb88110f6087710 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 16 Oct 2023 05:19:16 -0700 Subject: [PATCH 14/86] Fix version enum (#5352) * Fix version enum * Add license --- .../java/ca/uhn/fhir/util/VersionEnum.java | 4 ++++ .../{7_0_0 => 6_10_0}/3786-mdm-npe.yaml | 0 .../4834-ips-dont-include-empty-section.yaml | 0 ...89-clinical-reasoning-measurerefactor.yaml | 0 .../5090-adding-mdm-operation-pointcuts.yaml | 0 ...s-to-mdm-possible-duplicate-operation.yaml | 0 ...urces-will-link-to-existing-resources.yaml | 0 .../5192-include-in-search-paging-fix.yaml | 0 ...nge_operation_enabled-is-set-to-false.yaml | 0 ...ixing-everything-search-paging-issues.yaml | 0 ...ternal-server-when-context-is-missing.yaml | 0 .../5215-improve-hfql-id-filtering.yaml | 0 .../{7_0_0 => 6_10_0}/5219-batch-npe.yaml | 0 .../5220-race-conditions.yaml | 0 ...-transition-to-failed-status-on-mssql.yaml | 0 ...ink-history-gives-404-after-mdm-clear.yaml | 0 ...atient-ids-in-partitioned-environment.yaml | 0 ...lden-resource-after-changing-mdm-link.yaml | 0 .../5238-add-cds-on-fhir.yaml | 0 ...d-expunge-redirected-golden-resources.yaml | 0 .../5246-bulk-export-merge-steps.yaml | 0 ...led-disrupts-custom-resource-addition.yaml | 0 ...ter-when-doing-cleanuppossiblematches.yaml | 0 .../5259-sort-meta-collection-properties.yaml | 0 ...tch-to-the-mdm-history-link-operation.yaml | 0 .../5265-group-bulk-export-forward-refs.yaml | 0 ...method-outcome-from-resource-provider.yaml | 0 ...mprove-code-validation-error-messages.yaml | 0 .../5274-adding-metric-svc-to-mdm.yaml | 0 ...peration-with-access-to-all-resources.yaml | 0 ...llow-links-to-group-or-list-resources.yaml | 0 ...-request-results-in-an-http-400-error.yaml | 0 ...nditional-updates-to-invalidate-match.yaml | 0 ...295-fix-absolute-refs-with-identifier.yaml | 0 .../{7_0_0 => 6_10_0}/5297-ips-fixes.yaml | 0 .../5298-fix-everything-timeout.yaml | 0 ...om-r5-defaultprofilevalidationsupport.yaml | 0 .../5300-support-language-sp.yaml | 0 .../5306-add-no-history-mode.yaml | 0 ...ained-does-not-return-correct-results.yaml | 0 .../5310-broken-tag-parse.yaml | 0 ...p-failing-when-executing-on-partition.yaml | 0 ...date-on-same-resource-should-not-fail.yaml | 0 ...e-display-validation-now-configurable.yaml | 0 ...-validation-resources-are-out-of-date.yaml | 0 ...nt-needs-to-declare-conformance-to-ig.yaml | 0 ...operation-score-field-imprecise-value.yaml | 0 .../5333-prefix-regression-export.yaml | 0 ...provided-with-unknown-ids-on-postgres.yaml | 0 ...ameter-of-the-resource-is-not-present.yaml | 0 .../5341-add-cds-cr-registries.yaml | 0 ...e-failing-when-executing-on-partition.yaml | 0 ...49-jpa-config-bean-override-exception.yaml | 0 ...hen-used-with-non-iterate-revincludes.yaml | 0 .../5355-add-openapi-resource-details.yaml | 0 .../5356-cr-codecache-invalidation-bug.yaml | 0 .../changelog/{7_0_0 => 6_10_0}/changes.yaml | 0 .../changelog/{7_0_0 => 6_10_0}/upgrade.md | 0 .../changelog/{7_0_0 => 6_10_0}/version.yaml | 0 .../dao/expunge/PartitionAwareSupplier.java | 19 +++++++++++++++++++ 60 files changed, 23 insertions(+) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/3786-mdm-npe.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/4834-ips-dont-include-empty-section.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5089-clinical-reasoning-measurerefactor.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5090-adding-mdm-operation-pointcuts.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5141-updating-resources-will-link-to-existing-resources.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5192-include-in-search-paging-fix.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5198-fixing-everything-search-paging-issues.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5215-improve-hfql-id-filtering.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5219-batch-npe.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5220-race-conditions.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5236-rebuild-golden-resource-after-changing-mdm-link.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5238-add-cds-on-fhir.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5246-bulk-export-merge-steps.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5259-sort-meta-collection-properties.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5265-group-bulk-export-forward-refs.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5271-improve-code-validation-error-messages.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5274-adding-metric-svc-to-mdm.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5276-the-graphql-request-results-in-an-http-400-error.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5295-fix-absolute-refs-with-identifier.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5297-ips-fixes.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5298-fix-everything-timeout.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5300-support-language-sp.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5306-add-no-history-mode.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5310-broken-tag-parse.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5312-reindexstep-failing-when-executing-on-partition.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5321-code-display-validation-now-configurable.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5322-dstu3-validation-resources-are-out-of-date.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5331-mdm-query-links-operation-score-field-imprecise-value.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5333-prefix-regression-export.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5341-add-cds-cr-registries.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5344-expunge-failing-when-executing-on-partition.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5349-jpa-config-bean-override-exception.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5355-add-openapi-resource-details.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/5356-cr-codecache-invalidation-bug.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/changes.yaml (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/upgrade.md (100%) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{7_0_0 => 6_10_0}/version.yaml (100%) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index 0cf304f0c56..c8f5f564436 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -125,6 +125,10 @@ public enum VersionEnum { V6_8_2, V6_9_0, + v6_10_0, + + v6_11_0, + V7_0_0; public static VersionEnum latestVersion() { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/3786-mdm-npe.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/3786-mdm-npe.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/3786-mdm-npe.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/3786-mdm-npe.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/4834-ips-dont-include-empty-section.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4834-ips-dont-include-empty-section.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/4834-ips-dont-include-empty-section.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4834-ips-dont-include-empty-section.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5089-clinical-reasoning-measurerefactor.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5089-clinical-reasoning-measurerefactor.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5089-clinical-reasoning-measurerefactor.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5089-clinical-reasoning-measurerefactor.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5090-adding-mdm-operation-pointcuts.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5090-adding-mdm-operation-pointcuts.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5090-adding-mdm-operation-pointcuts.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5090-adding-mdm-operation-pointcuts.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5141-updating-resources-will-link-to-existing-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5141-updating-resources-will-link-to-existing-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5141-updating-resources-will-link-to-existing-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5141-updating-resources-will-link-to-existing-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5192-include-in-search-paging-fix.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5192-include-in-search-paging-fix.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5198-fixing-everything-search-paging-issues.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5198-fixing-everything-search-paging-issues.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5198-fixing-everything-search-paging-issues.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5198-fixing-everything-search-paging-issues.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5215-improve-hfql-id-filtering.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5215-improve-hfql-id-filtering.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5215-improve-hfql-id-filtering.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5215-improve-hfql-id-filtering.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5219-batch-npe.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5219-batch-npe.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5219-batch-npe.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5219-batch-npe.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5220-race-conditions.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5220-race-conditions.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5220-race-conditions.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5220-race-conditions.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5238-add-cds-on-fhir.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5238-add-cds-on-fhir.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5238-add-cds-on-fhir.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5246-bulk-export-merge-steps.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5246-bulk-export-merge-steps.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5246-bulk-export-merge-steps.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5246-bulk-export-merge-steps.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5259-sort-meta-collection-properties.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5259-sort-meta-collection-properties.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5259-sort-meta-collection-properties.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5259-sort-meta-collection-properties.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5265-group-bulk-export-forward-refs.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5265-group-bulk-export-forward-refs.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5265-group-bulk-export-forward-refs.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5265-group-bulk-export-forward-refs.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5271-improve-code-validation-error-messages.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5271-improve-code-validation-error-messages.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5274-adding-metric-svc-to-mdm.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5274-adding-metric-svc-to-mdm.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5274-adding-metric-svc-to-mdm.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5276-the-graphql-request-results-in-an-http-400-error.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5276-the-graphql-request-results-in-an-http-400-error.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5276-the-graphql-request-results-in-an-http-400-error.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5276-the-graphql-request-results-in-an-http-400-error.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5295-fix-absolute-refs-with-identifier.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5295-fix-absolute-refs-with-identifier.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5295-fix-absolute-refs-with-identifier.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5295-fix-absolute-refs-with-identifier.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5297-ips-fixes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5297-ips-fixes.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5297-ips-fixes.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5297-ips-fixes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5298-fix-everything-timeout.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5298-fix-everything-timeout.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5298-fix-everything-timeout.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5298-fix-everything-timeout.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-support-language-sp.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-support-language-sp.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-support-language-sp.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-support-language-sp.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5306-add-no-history-mode.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5306-add-no-history-mode.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5306-add-no-history-mode.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5306-add-no-history-mode.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5310-broken-tag-parse.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5310-broken-tag-parse.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5310-broken-tag-parse.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5310-broken-tag-parse.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5312-reindexstep-failing-when-executing-on-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5312-reindexstep-failing-when-executing-on-partition.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5312-reindexstep-failing-when-executing-on-partition.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5312-reindexstep-failing-when-executing-on-partition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5321-code-display-validation-now-configurable.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5321-code-display-validation-now-configurable.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5322-dstu3-validation-resources-are-out-of-date.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5322-dstu3-validation-resources-are-out-of-date.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5322-dstu3-validation-resources-are-out-of-date.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5322-dstu3-validation-resources-are-out-of-date.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5333-prefix-regression-export.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5333-prefix-regression-export.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5341-add-cds-cr-registries.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5341-add-cds-cr-registries.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5341-add-cds-cr-registries.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5344-expunge-failing-when-executing-on-partition.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5344-expunge-failing-when-executing-on-partition.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5344-expunge-failing-when-executing-on-partition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5349-jpa-config-bean-override-exception.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5349-jpa-config-bean-override-exception.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5349-jpa-config-bean-override-exception.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5355-add-openapi-resource-details.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5355-add-openapi-resource-details.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5355-add-openapi-resource-details.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5356-cr-codecache-invalidation-bug.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5356-cr-codecache-invalidation-bug.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5356-cr-codecache-invalidation-bug.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java index 156690acc0f..0be025f0355 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.expunge; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; From 25ce7d0b8f51b8a7c353ac4ccf5b0000e706d212 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Mon, 16 Oct 2023 12:16:58 -0700 Subject: [PATCH 15/86] fix broken version --- .../src/main/java/ca/uhn/fhir/util/VersionEnum.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index c8f5f564436..70f2a2bd4c7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -125,9 +125,9 @@ public enum VersionEnum { V6_8_2, V6_9_0, - v6_10_0, + V6_10_0, - v6_11_0, + V6_11_0, V7_0_0; From e2ca967fd987ceb506a75b85dd764895e0a98f14 Mon Sep 17 00:00:00 2001 From: michaelabuckley Date: Thu, 19 Oct 2023 09:18:01 -0400 Subject: [PATCH 16/86] Force _summary=false when fetching CodeSystems and ValueSets with remote terminology (#5372) Force _summary=false when fetching CodeSystems and ValueSets with remote terminology --- ...force-summary-false-remote-validation.yaml | 4 ++ ...teTerminologyServiceValidationSupport.java | 69 +++++++++++++++---- ...rminologyServiceValidationSupportTest.java | 34 ++++++++- 3 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml new file mode 100644 index 00000000000..d969e560b11 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5371 +title: "Remote terminology operations that fetch ValueSets or CodeSystems now force _summary=false to allow local validation." diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index 437ddb2358c..99a4b1b90c9 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.gclient.IQuery; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ParametersUtil; import org.apache.commons.lang3.StringUtils; @@ -24,6 +26,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -141,16 +144,32 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public IBaseResource fetchCodeSystem(String theSystem) { + // callers of this want the whole resource. + return fetchCodeSystem(theSystem, SummaryEnum.FALSE); + } + + /** + * Fetch the code system, possibly a summary. + * @param theSystem the canonical url + * @param theSummaryParam to force a summary mode - or null to allow server default. + * @return the CodeSystem + */ + @Nullable + private IBaseResource fetchCodeSystem(String theSystem, @Nullable SummaryEnum theSummaryParam) { IGenericClient client = provideClient(); Class bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); - IBaseBundle results = client.search() + IQuery codeSystemQuery = client.search() .forResource("CodeSystem") - .where(CodeSystem.URL.matches().value(theSystem)) - .returnBundle(bundleType) - .execute(); + .where(CodeSystem.URL.matches().value(theSystem)); + + if (theSummaryParam != null) { + codeSystemQuery.summaryMode(theSummaryParam); + } + + IBaseBundle results = codeSystemQuery.returnBundle(bundleType).execute(); List resultsList = BundleUtil.toListOfResources(myCtx, results); - if (resultsList.size() > 0) { + if (!resultsList.isEmpty()) { return resultsList.get(0); } @@ -388,16 +407,36 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public IBaseResource fetchValueSet(String theValueSetUrl) { + // force the remote server to send the whole resource. + SummaryEnum summaryParam = SummaryEnum.FALSE; + return fetchValueSet(theValueSetUrl, summaryParam); + } + + /** + * Search for a ValueSet by canonical url via IGenericClient. + * + * @param theValueSetUrl the canonical url of the ValueSet + * @param theSummaryParam force a summary mode - null allows server default + * @return the ValueSet or null if none match the url + */ + @Nullable + private IBaseResource fetchValueSet(String theValueSetUrl, SummaryEnum theSummaryParam) { IGenericClient client = provideClient(); Class bundleType = myCtx.getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class); - IBaseBundle results = client.search() + + IQuery valueSetQuery = client.search() .forResource("ValueSet") - .where(CodeSystem.URL.matches().value(theValueSetUrl)) - .returnBundle(bundleType) - .execute(); + .where(CodeSystem.URL.matches().value(theValueSetUrl)); + + if (theSummaryParam != null) { + valueSetQuery.summaryMode(theSummaryParam); + } + + IBaseBundle results = valueSetQuery.returnBundle(bundleType).execute(); + List resultsList = BundleUtil.toListOfResources(myCtx, results); - if (resultsList.size() > 0) { + if (!resultsList.isEmpty()) { return resultsList.get(0); } @@ -406,12 +445,18 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { - return fetchCodeSystem(theSystem) != null; + // a summary is ok if we are just checking the presence. + SummaryEnum summaryParam = null; + + return fetchCodeSystem(theSystem, summaryParam) != null; } @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { - return fetchValueSet(theValueSetUrl) != null; + // a summary is ok if we are just checking the presence. + SummaryEnum summaryParam = null; + + return fetchValueSet(theValueSetUrl, summaryParam) != null; } @Override diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java index 5f95cf86b97..99e8b754c3a 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IHttpRequest; @@ -175,6 +176,18 @@ public class RemoteTerminologyServiceValidationSupportTest { assertEquals(null, myValueSetProvider.myLastValueSet); } + @Test + void testFetchValueSet_forcesSummaryFalse() { + // given + myValueSetProvider.myNextReturnValueSets = new ArrayList<>(); + + // when + IBaseResource valueSet = mySvc.fetchValueSet(VALUE_SET_URL); + + // then + assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam); + } + @Test public void testValidateCodeWithAllParams_CodeSystem_Success() { createNextCodeSystemReturnParameters(true, DISPLAY, null); @@ -374,6 +387,19 @@ public class RemoteTerminologyServiceValidationSupportTest { assertNull(myConceptMapProvider.myLastReverse); } + @Test + void testFetchCodeSystem_forcesSummaryFalse() { + // given + myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>(); + + // when + IBaseResource codeSystem = mySvc.fetchCodeSystem("http://loinc.org"); + + // then + assertEquals(SummaryEnum.FALSE, myCodeSystemProvider.myLastSummaryParam); + } + + private void addMatchToTranslateRequest(Parameters params) { Parameters.ParametersParameterComponent matchParam = params.addParameter().setName("match"); matchParam.addPart().setName("equivalence").setValue(new CodeType(EQUIVALENCE_CODE)); @@ -626,6 +652,7 @@ public class RemoteTerminologyServiceValidationSupportTest { private static class MyCodeSystemProvider implements IResourceProvider { + private SummaryEnum myLastSummaryParam; private UriParam myLastUrlParam; private List myNextReturnCodeSystems; private int myInvocationCount; @@ -680,8 +707,9 @@ public class RemoteTerminologyServiceValidationSupportTest { } @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { + public List find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) { myLastUrlParam = theUrlParam; + myLastSummaryParam = theSummaryParam; assert myNextReturnCodeSystems != null; return myNextReturnCodeSystems; } @@ -703,6 +731,7 @@ public class RemoteTerminologyServiceValidationSupportTest { private StringType myLastDisplay; private ValueSet myLastValueSet; private UriParam myLastUrlParam; + private SummaryEnum myLastSummaryParam; @Operation(name = "validate-code", idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1), @@ -728,8 +757,9 @@ public class RemoteTerminologyServiceValidationSupportTest { } @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { + public List find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) { myLastUrlParam = theUrlParam; + myLastSummaryParam = theSummaryParam; assert myNextReturnValueSets != null; return myNextReturnValueSets; } From 20c8109a329a158f3b57f466ea7e764d3871cd53 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 20 Oct 2023 06:51:35 -0400 Subject: [PATCH 17/86] Speed up subscription triggering (#5377) * Improve subscription trigger speed * Speed up subscription triggering * Add changelogf * Spotless * Move changelog --- ...improve-subscription-triggering-speed.yaml | 5 + .../SubscriptionTriggeringSvcImpl.java | 342 +++++++++--------- .../SubscriptionTriggeringDstu3Test.java | 20 +- .../r4/FhirResourceDaoR4QueryCountTest.java | 124 +++++-- .../uhn/fhir/jpa/subscription/FhirR4Util.java | 10 +- .../message/MessageSubscriptionR4Test.java | 3 +- .../resthook/RestHookTestR4Test.java | 1 + .../CircularQueueCaptureQueriesListener.java | 107 +++--- .../resources/vm/jpa_spring_beans_java.vm | 1 - 9 files changed, 362 insertions(+), 251 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml new file mode 100644 index 00000000000..1ecda6db9a0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml @@ -0,0 +1,5 @@ +--- +type: perf +issue: 5377 +jira: SMILE-7545 +title: "Subscription triggering via the `$trigger-subscription` operation is now multi-threaded, which significantly improves performance for large data sets." diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java index 2cbd1c0d291..55563d08c71 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java @@ -51,11 +51,11 @@ import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; +import com.google.common.collect.Lists; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.time.DateUtils; -import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -77,6 +77,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -104,7 +105,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc private JpaStorageSettings myStorageSettings; @Autowired - private ISearchCoordinatorSvc mySearchCoordinatorSvc; + private ISearchCoordinatorSvc> mySearchCoordinatorSvc; @Autowired private MatchUrlService myMatchUrlService; @@ -240,13 +241,12 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc ourLog.info("Starting pass of subscription triggering job {}", theJobDetails.getJobId()); // Submit individual resources - int totalSubmitted = 0; - List>> futures = new ArrayList<>(); - while (theJobDetails.getRemainingResourceIds().size() > 0 && totalSubmitted < myMaxSubmitPerPass) { - totalSubmitted++; + AtomicInteger totalSubmitted = new AtomicInteger(0); + List> futures = new ArrayList<>(); + while (!theJobDetails.getRemainingResourceIds().isEmpty() && totalSubmitted.get() < myMaxSubmitPerPass) { + totalSubmitted.incrementAndGet(); String nextResourceId = theJobDetails.getRemainingResourceIds().remove(0); - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResourceId); - futures.add(Pair.of(nextResourceId, future)); + submitResource(theJobDetails.getSubscriptionId(), nextResourceId); } // Make sure these all succeeded in submitting @@ -260,7 +260,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc // to the broker. Note that querying of resource can be done synchronously or asynchronously if (isInitialStep(theJobDetails) && isNotEmpty(theJobDetails.getRemainingSearchUrls()) - && totalSubmitted < myMaxSubmitPerPass) { + && totalSubmitted.get() < myMaxSubmitPerPass) { String nextSearchUrl = theJobDetails.getRemainingSearchUrls().remove(0); RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, nextSearchUrl); @@ -295,144 +295,88 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc theJobDetails.setCurrentSearchLastUploadedIndex(-1); } - // processing step for synchronous processing mode - if (isNotBlank(theJobDetails.getCurrentSearchUrl()) && totalSubmitted < myMaxSubmitPerPass) { - List allCurrentResources; - - int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; - - String searchUrl = theJobDetails.getCurrentSearchUrl(); - - ourLog.info( - "Triggered job [{}] - Starting synchronous processing at offset {} and index {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentOffset(), - fromIndex); - - int submittableCount = myMaxSubmitPerPass - totalSubmitted; - int toIndex = fromIndex + submittableCount; - - if (nonNull(search) && !search.isEmpty()) { - - // we already have data from the initial step so process as much as we can. - ourLog.info("Triggered job[{}] will process up to {} resources", theJobDetails.getJobId(), toIndex); - allCurrentResources = search.getResources(0, toIndex); - - } else { - if (theJobDetails.getCurrentSearchCount() != null) { - toIndex = Math.min(toIndex, theJobDetails.getCurrentSearchCount()); - } - - RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, searchUrl); - String queryPart = searchUrl.substring(searchUrl.indexOf('?')); - SearchParameterMap params = myMatchUrlService.translateMatchUrl(queryPart, resourceDef); - int offset = theJobDetails.getCurrentOffset() + fromIndex; - params.setOffset(offset); - params.setCount(toIndex); - - ourLog.info( - "Triggered job[{}] requesting {} resources from offset {}", - theJobDetails.getJobId(), - toIndex, - offset); - - search = - mySearchService.executeQuery(resourceDef.getName(), params, RequestPartitionId.allPartitions()); - allCurrentResources = search.getAllResources(); - } - - ourLog.info( - "Triggered job[{}] delivering {} resources", theJobDetails.getJobId(), allCurrentResources.size()); - int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex(); - - for (IBaseResource nextResource : allCurrentResources) { - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResource); - futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future)); - totalSubmitted++; - highestIndexSubmitted++; - } - - if (validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { - return; - } - - theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted); - - ourLog.info( - "Triggered job[{}] lastUploadedIndex is {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchLastUploadedIndex()); - - if (allCurrentResources.isEmpty() - || nonNull(theJobDetails.getCurrentSearchCount()) - && toIndex >= theJobDetails.getCurrentSearchCount()) { - ourLog.info( - "Triggered job[{}] for search URL {} has completed ", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchUrl()); - theJobDetails.setCurrentSearchResourceType(null); - theJobDetails.clearCurrentSearchUrl(); - theJobDetails.setCurrentSearchLastUploadedIndex(-1); - theJobDetails.setCurrentSearchCount(null); - } + /* + * Processing step for synchronous processing mode - This is only called if the + * server is configured to force offset searches, ie using ForceSynchronousSearchInterceptor. + * Otherwise, we'll always do async mode. + */ + if (isNotBlank(theJobDetails.getCurrentSearchUrl()) && totalSubmitted.get() < myMaxSubmitPerPass) { + processSynchronous(theJobDetails, totalSubmitted, futures, search); } // processing step for asynchronous processing mode - if (isNotBlank(theJobDetails.getCurrentSearchUuid()) && totalSubmitted < myMaxSubmitPerPass) { - int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; + if (isNotBlank(theJobDetails.getCurrentSearchUuid()) && totalSubmitted.get() < myMaxSubmitPerPass) { + processAsynchronous(theJobDetails, totalSubmitted, futures); + } - IFhirResourceDao resourceDao = - myDaoRegistry.getResourceDao(theJobDetails.getCurrentSearchResourceType()); + ourLog.info( + "Subscription trigger job[{}] triggered {} resources in {}ms ({} res / second)", + theJobDetails.getJobId(), + totalSubmitted, + sw.getMillis(), + sw.getThroughput(totalSubmitted.get(), TimeUnit.SECONDS)); + } - int maxQuerySize = myMaxSubmitPerPass - totalSubmitted; - int toIndex; - if (theJobDetails.getCurrentSearchCount() != null) { - toIndex = Math.min(fromIndex + maxQuerySize, theJobDetails.getCurrentSearchCount()); - } else { - toIndex = fromIndex + maxQuerySize; - } + private void processAsynchronous( + SubscriptionTriggeringJobDetails theJobDetails, AtomicInteger totalSubmitted, List> futures) { + int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; - ourLog.info( - "Triggering job[{}] search {} requesting resources {} - {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchUuid(), - fromIndex, - toIndex); + IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theJobDetails.getCurrentSearchResourceType()); - List> resourceIds; - RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions(); - resourceIds = mySearchCoordinatorSvc.getResources( - theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId); + int maxQuerySize = myMaxSubmitPerPass - totalSubmitted.get(); + int toIndex; + if (theJobDetails.getCurrentSearchCount() != null) { + toIndex = Math.min(fromIndex + maxQuerySize, theJobDetails.getCurrentSearchCount()); + } else { + toIndex = fromIndex + maxQuerySize; + } - ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), resourceIds.size()); - int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex(); + ourLog.info( + "Triggering job[{}] search {} requesting resources {} - {}", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchUuid(), + fromIndex, + toIndex); - String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType()); - RuntimeResourceDefinition resourceDef = - myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType()); - ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder( - resourceDao, resourceType, resourceDef.getImplementingClass()); - List listToPopulate = new ArrayList<>(); + List> allResourceIds; + RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions(); + allResourceIds = mySearchCoordinatorSvc.getResources( + theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId); - myTransactionService.withSystemRequest().execute(() -> { - searchBuilder.loadResourcesByPid( - resourceIds, Collections.emptyList(), listToPopulate, false, new SystemRequestDetails()); - }); + ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), allResourceIds.size()); + AtomicInteger highestIndexSubmitted = new AtomicInteger(theJobDetails.getCurrentSearchLastUploadedIndex()); - for (IBaseResource nextResource : listToPopulate) { - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResource); - futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future)); - totalSubmitted++; - highestIndexSubmitted++; - } + List>> partitions = Lists.partition(allResourceIds, 100); + for (List> resourceIds : partitions) { + Runnable job = () -> { + String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType()); + RuntimeResourceDefinition resourceDef = + myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType()); + ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder( + resourceDao, resourceType, resourceDef.getImplementingClass()); + List listToPopulate = new ArrayList<>(); - if (validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { - return; - } + myTransactionService.withRequest(null).execute(() -> { + searchBuilder.loadResourcesByPid( + resourceIds, Collections.emptyList(), listToPopulate, false, new SystemRequestDetails()); + }); - theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted); + for (IBaseResource nextResource : listToPopulate) { + submitResource(theJobDetails.getSubscriptionId(), nextResource); + totalSubmitted.incrementAndGet(); + highestIndexSubmitted.incrementAndGet(); + } + }; - if (resourceIds.size() == 0 + Future future = myExecutorService.submit(job); + futures.add(future); + } + + if (!validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { + + theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted.get()); + + if (allResourceIds.isEmpty() || (theJobDetails.getCurrentSearchCount() != null && toIndex >= theJobDetails.getCurrentSearchCount())) { ourLog.info( @@ -445,13 +389,93 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc theJobDetails.setCurrentSearchCount(null); } } + } + + private void processSynchronous( + SubscriptionTriggeringJobDetails theJobDetails, + AtomicInteger totalSubmitted, + List> futures, + IBundleProvider search) { + List allCurrentResources; + + int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; + + String searchUrl = theJobDetails.getCurrentSearchUrl(); ourLog.info( - "Subscription trigger job[{}] triggered {} resources in {}ms ({} res / second)", + "Triggered job [{}] - Starting synchronous processing at offset {} and index {}", theJobDetails.getJobId(), - totalSubmitted, - sw.getMillis(), - sw.getThroughput(totalSubmitted, TimeUnit.SECONDS)); + theJobDetails.getCurrentOffset(), + fromIndex); + + int submittableCount = myMaxSubmitPerPass - totalSubmitted.get(); + int toIndex = fromIndex + submittableCount; + + if (nonNull(search) && !search.isEmpty()) { + + if (search.getCurrentPageSize() != null) { + toIndex = search.getCurrentPageSize(); + } + + // we already have data from the initial step so process as much as we can. + ourLog.info("Triggered job[{}] will process up to {} resources", theJobDetails.getJobId(), toIndex); + allCurrentResources = search.getResources(0, toIndex); + + } else { + if (theJobDetails.getCurrentSearchCount() != null) { + toIndex = Math.min(toIndex, theJobDetails.getCurrentSearchCount()); + } + + RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, searchUrl); + String queryPart = searchUrl.substring(searchUrl.indexOf('?')); + SearchParameterMap params = myMatchUrlService.translateMatchUrl(queryPart, resourceDef); + int offset = theJobDetails.getCurrentOffset() + fromIndex; + params.setOffset(offset); + params.setCount(toIndex); + + ourLog.info( + "Triggered job[{}] requesting {} resources from offset {}", + theJobDetails.getJobId(), + toIndex, + offset); + + search = mySearchService.executeQuery(resourceDef.getName(), params, RequestPartitionId.allPartitions()); + allCurrentResources = search.getResources(0, submittableCount); + } + + ourLog.info("Triggered job[{}] delivering {} resources", theJobDetails.getJobId(), allCurrentResources.size()); + AtomicInteger highestIndexSubmitted = new AtomicInteger(theJobDetails.getCurrentSearchLastUploadedIndex()); + + for (IBaseResource nextResource : allCurrentResources) { + Future future = + myExecutorService.submit(() -> submitResource(theJobDetails.getSubscriptionId(), nextResource)); + futures.add(future); + totalSubmitted.incrementAndGet(); + highestIndexSubmitted.incrementAndGet(); + } + + if (!validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { + + theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted.get()); + + ourLog.info( + "Triggered job[{}] lastUploadedIndex is {}", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchLastUploadedIndex()); + + if (allCurrentResources.isEmpty() + || nonNull(theJobDetails.getCurrentSearchCount()) + && toIndex > theJobDetails.getCurrentSearchCount()) { + ourLog.info( + "Triggered job[{}] for search URL {} has completed ", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchUrl()); + theJobDetails.setCurrentSearchResourceType(null); + theJobDetails.clearCurrentSearchUrl(); + theJobDetails.setCurrentSearchLastUploadedIndex(-1); + theJobDetails.setCurrentSearchCount(null); + } + } } private boolean isInitialStep(SubscriptionTriggeringJobDetails theJobDetails) { @@ -462,34 +486,31 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc return isInitialStep(theJobDetails); } - private boolean validateFuturesAndReturnTrueIfWeShouldAbort(List>> theIdToFutures) { + private boolean validateFuturesAndReturnTrueIfWeShouldAbort(List> theFutures) { - for (Pair> next : theIdToFutures) { - String nextDeliveredId = next.getKey(); + for (Future nextFuture : theFutures) { try { - Future nextFuture = next.getValue(); nextFuture.get(); - ourLog.info("Finished redelivering {}", nextDeliveredId); } catch (Exception e) { - ourLog.error("Failure triggering resource " + nextDeliveredId, e); + ourLog.error("Failure triggering resource", e); return true; } } // Clear the list since it will potentially get reused - theIdToFutures.clear(); + theFutures.clear(); return false; } - private Future submitResource(String theSubscriptionId, String theResourceIdToTrigger) { + private void submitResource(String theSubscriptionId, String theResourceIdToTrigger) { org.hl7.fhir.r4.model.IdType resourceId = new org.hl7.fhir.r4.model.IdType(theResourceIdToTrigger); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceId.getResourceType()); IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartitions()); - return submitResource(theSubscriptionId, resourceToTrigger); + submitResource(theSubscriptionId, resourceToTrigger); } - private Future submitResource(String theSubscriptionId, IBaseResource theResourceToTrigger) { + private void submitResource(String theSubscriptionId, IBaseResource theResourceToTrigger) { ourLog.info( "Submitting resource {} to subscription {}", @@ -500,24 +521,23 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc myFhirContext, theResourceToTrigger, ResourceModifiedMessage.OperationTypeEnum.UPDATE); msg.setSubscriptionId(theSubscriptionId); - return myExecutorService.submit(() -> { - for (int i = 0; ; i++) { - try { - myResourceModifiedConsumer.submitResourceModified(msg); - break; - } catch (Exception e) { - if (i >= 3) { - throw new InternalErrorException(Msg.code(25) + e); - } + for (int i = 0; ; i++) { + try { + myResourceModifiedConsumer.submitResourceModified(msg); + break; + } catch (Exception e) { + if (i >= 3) { + throw new InternalErrorException(Msg.code(25) + e); + } - ourLog.warn( - "Exception while retriggering subscriptions (going to sleep and retry): {}", e.toString()); + ourLog.warn("Exception while retriggering subscriptions (going to sleep and retry): {}", e.toString()); + try { Thread.sleep(1000); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); } } - - return null; - }); + } } public void cancelAll() { diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java index 79c2f798c1c..16961762a24 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java @@ -44,6 +44,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.http.HttpServletRequest; @@ -499,14 +501,18 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te myInterceptorService.unregisterInterceptor(forceSynchronousSearchInterceptor); } - @Test - public void testTriggerSubscriptionInSynchronousQueryMode() throws Exception { - ((SubscriptionTriggeringSvcImpl)mySubscriptionTriggeringSvc).setMaxSubmitPerPass(10); + @ParameterizedTest + @CsvSource({ + "10", + "10000" + }) + public void testTriggerSubscriptionInSynchronousQueryMode(int theMaxSubmitPerpass) throws Exception { + ((SubscriptionTriggeringSvcImpl)mySubscriptionTriggeringSvc).setMaxSubmitPerPass(theMaxSubmitPerpass); String payload = "application/fhir+json"; IdType sub2id = createSubscription("Patient?", payload, ourListenerServerBase).getIdElement(); - int numberOfPatient = 15; + int numberOfPatient = 200; // Create lots createPatientsAndWait(numberOfPatient); @@ -522,9 +528,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te .withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?")) .execute(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - mySubscriptionTriggeringSvc.runDeliveryPass(); + for (int i = 0; i < 20; i++) { + mySubscriptionTriggeringSvc.runDeliveryPass(); + } waitForSize(0, ourCreatedPatients); waitForSize(numberOfPatient, ourUpdatedPatients); 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 826a7b382c3..e1a535b678b 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 @@ -19,6 +19,7 @@ import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; 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; @@ -27,7 +28,9 @@ import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc; +import ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl; import ca.uhn.fhir.jpa.term.TermReadSvcImpl; +import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil; import ca.uhn.fhir.jpa.util.SqlQuery; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; @@ -40,9 +43,12 @@ import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +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 org.hamcrest.CoreMatchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; @@ -94,12 +100,16 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static ca.uhn.fhir.jpa.subscription.FhirR4Util.createSubscription; import static org.apache.commons.lang3.StringUtils.countMatches; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -145,6 +155,9 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test private ReindexStep myReindexStep; @Autowired private DeleteExpungeStep myDeleteExpungeStep; + @Autowired + protected SubscriptionTestUtil mySubscriptionTestUtil; + @AfterEach public void afterResetDao() { @@ -167,6 +180,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myFhirContext.getParserOptions().setStripVersionsFromReferences(true); TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + + mySubscriptionTestUtil.unregisterSubscriptionInterceptor(); } @Override @@ -3073,8 +3088,6 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test Bundle input = bb.getBundleTyped(); -// input.getEntry().get(0). - myCaptureQueriesListener.clear(); mySystemDao.transaction(mySrd, input); assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); @@ -3087,37 +3100,102 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test */ @SuppressWarnings("unchecked") @Test - public void testTriggerSubscription() throws Exception { + public void testTriggerSubscription_Sync() throws Exception { // Setup + IntStream.range(0, 200).forEach(i->createAPatient()); - myStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); - myResourceModifiedSubmitterSvc.startIfNeeded(); + mySubscriptionTestUtil.registerRestHookInterceptor(); + ForceOffsetSearchModeInterceptor interceptor = new ForceOffsetSearchModeInterceptor(); + myInterceptorRegistry.registerInterceptor(interceptor); + try { + String payload = "application/fhir+json"; + Subscription subscription = createSubscription("Patient?", payload, ourServer.getBaseUrl(), null); + IIdType subscriptionId = mySubscriptionDao.create(subscription, mySrd).getId(); - for (int i = 0; i < 10; i++) { - createPatient(withActiveTrue()); + waitForActivatedSubscriptionCount(1); + + mySubscriptionTriggeringSvc.triggerSubscription(null, List.of(new StringType("Patient?")), subscriptionId); + + // Test + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + myCaptureQueriesListener.logSelectQueries(); + ourPatientProvider.waitForUpdateCount(200); + + // Validate + assertEquals(7, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + } finally { + myInterceptorRegistry.unregisterInterceptor(interceptor); } + } + + + @Test + public void testTriggerSubscription_Async() throws Exception { + // Setup + IntStream.range(0, 200).forEach(i->createAPatient()); + + mySubscriptionTestUtil.registerRestHookInterceptor(); + + String payload = "application/fhir+json"; + Subscription subscription = createSubscription("Patient?", payload, ourServer.getBaseUrl(), null); + IIdType subId = mySubscriptionDao.create(subscription, mySrd).getId(); - Subscription subscription = new Subscription(); - subscription.getChannel().setEndpoint(ourServer.getBaseUrl()); - subscription.getChannel().setType(Subscription.SubscriptionChannelType.RESTHOOK); - subscription.getChannel().setPayload(Constants.CT_FHIR_JSON_NEW); - subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); - subscription.setCriteria("Patient?active=true"); - IIdType subscriptionId = mySubscriptionDao.create(subscription, mySrd).getId().toUnqualifiedVersionless(); waitForActivatedSubscriptionCount(1); - mySubscriptionTriggeringSvc.triggerSubscription(null, List.of(new StringType("Patient?active=true")), subscriptionId); - // Test myCaptureQueriesListener.clear(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - ourPatientProvider.waitForUpdateCount(10); + Parameters response = myClient + .operation() + .onInstance(subId) + .named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION) + .withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?")) + .execute(); + String responseValue = response.getParameter().get(0).getValue().primitiveValue(); + assertThat(responseValue, CoreMatchers.containsString("Subscription triggering job submitted as JOB ID")); - // Validate - assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); - assertEquals(11, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + assertEquals(3, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + myCaptureQueriesListener.logInsertQueries(); + assertEquals(15, myCaptureQueriesListener.countSelectQueries()); + assertEquals(201, myCaptureQueriesListener.countInsertQueries()); + assertEquals(3, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + assertEquals(2, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + assertEquals(0, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + SubscriptionTriggeringSvcImpl svc = ProxyUtil.getSingletonTarget(mySubscriptionTriggeringSvc, SubscriptionTriggeringSvcImpl.class); + assertEquals(0, svc.getActiveJobCount()); + + assertEquals(0, ourPatientProvider.getCountCreate()); + await().until(ourPatientProvider::getCountUpdate, equalTo(200L)); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java index fa8ea305503..31f92581c61 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java @@ -12,12 +12,14 @@ import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Subscription; +import javax.annotation.Nullable; + public class FhirR4Util { public static final String LPI_CODESYSTEM = "http://cognitivemedicine.com/lpi"; public static final String LPI_CODE = "LPI-FHIR"; - public static Subscription createSubscription(String criteria, String payload, String endpoint, IGenericClient client) { + public static Subscription createSubscription(String criteria, String payload, String endpoint, @Nullable IGenericClient client) { Subscription subscription = new Subscription(); subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)"); subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); @@ -29,8 +31,10 @@ public class FhirR4Util { channel.setEndpoint(endpoint); subscription.setChannel(channel); - MethodOutcome methodOutcome = client.create().resource(subscription).execute(); - subscription.setId(methodOutcome.getId().getIdPart()); + if (client != null) { + MethodOutcome methodOutcome = client.create().resource(subscription).execute(); + subscription.setId(methodOutcome.getId().getIdPart()); + } return subscription; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java index 540f5ae1f7d..2fc3ca8bb20 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java @@ -79,7 +79,8 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test { myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode()); } - @BeforeEach + @Override + @BeforeEach public void beforeRegisterRestHookListener() { mySubscriptionTestUtil.registerMessageInterceptor(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java index ace7684317f..29163d8018c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.test.util.StoppableSubscriptionDeliveringRestHookSubscriber; +import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil; import ca.uhn.fhir.jpa.topic.SubscriptionTopicDispatcher; import ca.uhn.fhir.jpa.topic.SubscriptionTopicRegistry; import ca.uhn.fhir.model.primitive.IdDt; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java index 3251fc242f0..ed355cdc3fa 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java @@ -208,40 +208,31 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured UPDATE queries */ public String logUpdateQueriesForCurrentThread() { - List queries = getUpdateQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String joined = String.join("\n", queries); + List queries = getUpdateQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String joined = String.join("\n", queriesStrings); ourLog.info("Update Queries:\n{}", joined); return joined; } /** * Log all captured SELECT queries - * - * @return */ public String logSelectQueriesForCurrentThread(int... theIndexes) { - List queries = getSelectQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); + List queries = getSelectQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); List newList = new ArrayList<>(); if (theIndexes != null && theIndexes.length > 0) { - for (int i = 0; i < theIndexes.length; i++) { - int index = theIndexes[i]; - newList.add("[" + index + "] " + queries.get(index)); - } - } else { - for (int i = 0; i < queries.size(); i++) { - newList.add("[" + i + "] " + queries.get(i)); + for (int index : theIndexes) { + newList.add(queriesStrings.get(index)); } + queriesStrings = newList; } - queries = newList; - String queriesAsString = String.join("\n", queries); - ourLog.info("Select Queries:\n{}", queriesAsString); - return queriesAsString; + String joined = String.join("\n", queriesStrings); + ourLog.info("Select Queries:\n{}", joined); + return joined; } /** @@ -256,20 +247,32 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe */ public List logSelectQueries(boolean theInlineParams, boolean theFormatSql) { List queries = getSelectQueries(); - List queriesStrings = queries.stream() - .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, theInlineParams, theFormatSql)) - .collect(Collectors.toList()); + List queriesStrings = renderQueriesForLogging(theInlineParams, theFormatSql, queries); ourLog.info("Select Queries:\n{}", String.join("\n", queriesStrings)); return queries; } + @Nonnull + private static List renderQueriesForLogging( + boolean theInlineParams, boolean theFormatSql, List queries) { + List queriesStrings = new ArrayList<>(); + for (int i = 0; i < queries.size(); i++) { + SqlQuery query = queries.get(i); + String remderedString = "[" + i + "] " + + CircularQueueCaptureQueriesListener.formatQueryAsSql(query, theInlineParams, theFormatSql); + queriesStrings.add(remderedString); + } + return queriesStrings; + } + /** * Log first captured SELECT query */ public void logFirstSelectQueryForCurrentThread() { + boolean inlineParams = true; String firstSelectQuery = getSelectQueriesForCurrentThread().stream() .findFirst() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) + .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, inlineParams, inlineParams)) .orElse("NONE FOUND"); ourLog.info("First select SqlQuery:\n{}", firstSelectQuery); } @@ -278,10 +281,9 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public String logInsertQueriesForCurrentThread() { - List queries = getInsertQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String queriesAsString = String.join("\n", queries); + List queries = getInsertQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String queriesAsString = String.join("\n", queriesStrings); ourLog.info("Insert Queries:\n{}", queriesAsString); return queriesAsString; } @@ -290,20 +292,18 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured queries */ public void logAllQueriesForCurrentThread() { - List queries = getAllQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Queries:\n{}", String.join("\n", queries)); + List queries = getAllQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Queries:\n{}", String.join("\n", queriesStrings)); } /** * Log all captured queries */ public void logAllQueries() { - List queries = getCapturedQueries().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Queries:\n{}", String.join("\n", queries)); + List queries = getCapturedQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Queries:\n{}", String.join("\n", queriesStrings)); } /** @@ -317,10 +317,12 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public int logInsertQueries(Predicate theInclusionPredicate) { - List insertQueries = getInsertQueries(); - List queries = insertQueries.stream() + List insertQueries = getInsertQueries().stream() .filter(t -> theInclusionPredicate == null || theInclusionPredicate.test(t)) - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) + .collect(Collectors.toList()); + boolean inlineParams = true; + List queries = insertQueries.stream() + .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, inlineParams, inlineParams)) .collect(Collectors.toList()); ourLog.info("Insert Queries:\n{}", String.join("\n", queries)); @@ -331,23 +333,20 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public int logUpdateQueries() { - List updateQueries = getUpdateQueries(); - List queries = updateQueries.stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Update Queries:\n{}", String.join("\n", queries)); + List queries = getUpdateQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Update Queries:\n{}", String.join("\n", queriesStrings)); - return countQueries(updateQueries); + return countQueries(queries); } /** * Log all captured DELETE queries */ public String logDeleteQueriesForCurrentThread() { - List queries = getDeleteQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String joined = String.join("\n", queries); + List queries = getDeleteQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String joined = String.join("\n", queriesStrings); ourLog.info("Delete Queries:\n{}", joined); return joined; } @@ -356,13 +355,11 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured DELETE queries */ public int logDeleteQueries() { - List deleteQueries = getDeleteQueries(); - List queries = deleteQueries.stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Delete Queries:\n{}", String.join("\n", queries)); + List queries = getDeleteQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Delete Queries:\n{}", String.join("\n", queriesStrings)); - return countQueries(deleteQueries); + return countQueries(queries); } public int countSelectQueries() { diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm index 1cb6f5e6102..a3e2d44a42d 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans_java.vm @@ -5,7 +5,6 @@ import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import javax.persistence.EntityManager; import org.springframework.transaction.PlatformTransactionManager; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; From af1d46fb04b2b0aac5dd9d2ab0431ff6a59751a5 Mon Sep 17 00:00:00 2001 From: TipzCM Date: Fri, 20 Oct 2023 09:56:32 -0400 Subject: [PATCH 18/86] adding additional logging to resource modified submitter svc (#5370) * adding additional logging to resource modified submitter svc * s[otlestt * review fixes * spotless * fixing test --------- Co-authored-by: leif stawnyczy --- .../svc/ResourceModifiedSubmitterSvc.java | 31 +++-- .../svc/ResourceModifiedSubmitterSvcTest.java | 112 +++++++++++++++++- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java index 7d768beefce..99d291959ad 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java @@ -145,8 +145,8 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, return theStatus -> { boolean processed = true; ResourceModifiedMessage resourceModifiedMessage = null; - try { + try { // delete the entry to lock the row to ensure unique processing boolean wasDeleted = deletePersistedResourceModifiedMessage( thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk()); @@ -159,15 +159,27 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, resourceModifiedMessage = optionalResourceModifiedMessage.get(); submitResourceModified(resourceModifiedMessage); } - } catch (MessageDeliveryException exception) { // we encountered an issue when trying to send the message so mark the transaction for rollback + String payloadId = "[unknown]"; + String subscriptionId = "[unknown]"; + if (resourceModifiedMessage != null) { + payloadId = resourceModifiedMessage.getPayloadId(); + subscriptionId = resourceModifiedMessage.getSubscriptionId(); + } ourLog.error( "Channel submission failed for resource with id {} matching subscription with id {}. Further attempts will be performed at later time.", - resourceModifiedMessage.getPayloadId(), - resourceModifiedMessage.getSubscriptionId()); + payloadId, + subscriptionId, + exception); processed = false; theStatus.setRollbackOnly(); + } catch (Exception ex) { + // catch other errors + ourLog.error( + "Unexpected error encountered while processing resource modified message. Marking as processed to prevent further errors.", + ex); + processed = true; } return processed; @@ -179,7 +191,6 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, ResourceModifiedMessage resourceModifiedMessage = null; try { - resourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage( thePersistedResourceModifiedMessage); @@ -193,14 +204,17 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, persistedResourceModifiedMessagePk.getResourceVersion()); ourLog.warn( - "Scheduled submission will be ignored since resource {} cannot be found", idType.asStringValue()); + "Scheduled submission will be ignored since resource {} cannot be found", + idType.asStringValue(), + e); + } catch (Exception ex) { + ourLog.error("Unknown error encountered on inflation of resources.", ex); } return Optional.ofNullable(resourceModifiedMessage); } private boolean deletePersistedResourceModifiedMessage(IPersistedResourceModifiedMessagePK theResourceModifiedPK) { - try { // delete the entry to lock the row to ensure unique processing return myResourceModifiedMessagePersistenceSvc.deleteByPK(theResourceModifiedPK); @@ -213,6 +227,9 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, // the message // successfully before we did. + return false; + } catch (Exception ex) { + ourLog.error("Unknown exception when deleting persisted resource modified message. Returning false.", ex); return false; } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java index 872ec955c81..21d1f97c686 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.subscription.svc; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.model.entity.PersistedResourceModifiedMessageEntityPK; import ca.uhn.fhir.jpa.model.entity.ResourceModifiedEntity; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; @@ -9,7 +10,12 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFact import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,15 +25,24 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.SimpleTransactionStatus; +import java.util.List; +import java.util.Optional; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +50,8 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ResourceModifiedSubmitterSvcTest { + private final ch.qos.logback.classic.Logger ourLogger = (Logger) LoggerFactory.getLogger(ResourceModifiedSubmitterSvc.class); + @Mock StorageSettings myStorageSettings; @Mock @@ -46,6 +63,9 @@ public class ResourceModifiedSubmitterSvcTest { @Mock IChannelProducer myChannelProducer; + @Mock + ListAppender myListAppender; + ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc; TransactionStatus myCapturingTransactionStatus; @@ -81,7 +101,7 @@ public class ResourceModifiedSubmitterSvcTest { } @Test - public void testSubmitPersisedResourceModifiedMessage_withExistingPersistedResourceModifiedMessage_willSucceed(){ + public void testSubmitPersistedResourceModifiedMessage_withExistingPersistedResourceModifiedMessage_willSucceed(){ // given // a successful deletion implies that the message did exist. when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); @@ -94,11 +114,95 @@ public class ResourceModifiedSubmitterSvcTest { assertThat(wasProcessed, is(Boolean.TRUE)); assertThat(myCapturingTransactionStatus.isRollbackOnly(), is(Boolean.FALSE)); verify(myChannelProducer, times(1)).send(any()); - } @Test - public void testSubmitPersisedResourceModifiedMessage_whenMessageWasAlreadyProcess_willSucceed(){ + public void testSubmitPersistedResource_logsDeleteAndInflationExceptions() { + // setup + String deleteExMsg = "Delete Exception"; + String inflationExMsg = "Inflation Exception"; + String patientId = "/Patient/123/_history/1"; + ResourceModifiedEntity resourceModified = new ResourceModifiedEntity(); + PersistedResourceModifiedMessageEntityPK rpm = new PersistedResourceModifiedMessageEntityPK(); + rpm.setResourcePid(patientId); + rpm.setResourceVersion("1"); + resourceModified.setResourceModifiedEntityPK(rpm); + + ourLogger.addAppender(myListAppender); + ourLogger.setLevel(Level.ERROR); + + // when + when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) + .thenThrow(new RuntimeException(deleteExMsg)); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())) + .thenThrow(new RuntimeException(inflationExMsg)); + + // test + boolean processed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(resourceModified); + + // verify + assertTrue(processed); + + ArgumentCaptor logEvent = ArgumentCaptor.forClass(ILoggingEvent.class); + verify(myListAppender, atLeast(2)) + .doAppend(logEvent.capture()); + + List logs = logEvent.getAllValues(); + boolean hasDeleteException = false; + boolean hasInflationException = false; + for (ILoggingEvent log : logs) { + if (log.getThrowableProxy().getMessage().contains(deleteExMsg)) { + hasDeleteException = true; + } + if (log.getThrowableProxy().getMessage().contains(inflationExMsg)) { + hasInflationException = true; + } + } + assertTrue(hasDeleteException); + assertTrue(hasInflationException); + } + + @Test + public void testSubmitPersistedResource_withMissingResource_processes() { + // setup + String patientId = "Patient/123"; + String exceptionString = "A random exception"; + ResourceModifiedEntity resourceModified = new ResourceModifiedEntity(); + PersistedResourceModifiedMessageEntityPK rpm = new PersistedResourceModifiedMessageEntityPK(); + rpm.setResourcePid(patientId); + rpm.setResourceVersion("1"); + resourceModified.setResourceModifiedEntityPK(rpm); + ResourceModifiedMessage msg = new ResourceModifiedMessage(); + + ourLogger.addAppender(myListAppender); + ourLogger.setLevel(Level.ERROR); + + // when + when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) + .thenReturn(true); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())) + .thenReturn(msg); + when(myChannelProducer.send(any())) + .thenThrow(new RuntimeException(exceptionString)); + + // test + boolean processed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(resourceModified); + + // then + assertTrue(processed); + + // verify + verify(myChannelProducer) + .send(any()); + ArgumentCaptor loggingCaptor = ArgumentCaptor.forClass(ILoggingEvent.class); + verify(myListAppender).doAppend(loggingCaptor.capture()); + ILoggingEvent event = loggingCaptor.getValue(); + assertNotNull(event); + assertTrue(event.getThrowableProxy().getMessage().contains(exceptionString)); + } + + @Test + public void testSubmitPersistedResourceModifiedMessage_whenMessageWasAlreadyProcess_willSucceed(){ // given // deletion fails, someone else was faster and processed the message when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(false); @@ -116,7 +220,7 @@ public class ResourceModifiedSubmitterSvcTest { } @Test - public void testSubmitPersisedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){ + public void testSubmitPersistedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){ // given when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); From ee369269f8ec73b3371881e8b5ef8e348a6cbd54 Mon Sep 17 00:00:00 2001 From: michaelabuckley Date: Fri, 20 Oct 2023 12:14:31 -0400 Subject: [PATCH 19/86] Move forced-id to HFJ_RESOURCE - step 2 (#4803) * First cut at forced-id step 2 Start using new fhir_id column in hfj_resource instead. * demote fixme for build * Start changelog * Merge cleanups * forced-id migration various fixme cleanups * checkstyle * fix bad conversions to computeIfAbsent * Ugh. Lame checkstyle. * Revert optimistic null assert * missed import. * Fixup broken tests * Fix invalid test * Add missing index annotation --- .../6_8_0/4803-forced-id-step-2.yaml | 4 + .../config/HapiFhirHibernateJpaDialect.java | 42 +++++----- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 1 + .../uhn/fhir/jpa/dao/data/IForcedIdDao.java | 20 +++-- .../fhir/jpa/dao/data/IResourceTableDao.java | 4 +- ...aoImpl.java => IResourceTableDaoImpl.java} | 41 +++++----- .../fhir/jpa/dao/index/IdHelperService.java | 81 ++++++++----------- .../BulkExportCollectionFileEntity.java | 4 +- .../fhir/jpa/entity/ResourceSearchView.java | 39 ++++----- .../tasks/HapiFhirJpaMigrationTasks.java | 14 ++++ .../jpa/model/cross/JpaResourceLookup.java | 2 +- .../jpa/search/reindex/ResourceReindexer.java | 22 ----- .../reindex/ResourceReindexingSvcImpl.java | 4 - .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 8 +- .../jpa/model/entity/BaseHasResource.java | 1 + .../uhn/fhir/jpa/model/entity/ForcedId.java | 1 - .../model/entity/ResourceHistoryTable.java | 7 +- .../fhir/jpa/model/entity/ResourceTable.java | 40 +++++---- .../jpa/model/entity/ResourceTableTest.java | 2 +- .../dao/dstu3/FhirResourceDaoDstu3Test.java | 4 +- .../bulk/imprt2/ConsumeFilesStepR4Test.java | 4 +- .../jpa/dao/index/IdHelperServiceTest.java | 8 +- .../jpa/dao/r4/BasePartitioningR4Test.java | 2 + .../FhirResourceDaoR4SearchOptimizedTest.java | 12 +-- .../PatientIdPartitionInterceptorTest.java | 4 +- .../provider/r4/MultitenantServerR4Test.java | 32 ++++---- .../ResourceReindexingSvcImplTest.java | 3 - .../uhn/fhir/jpa/term/ITermReadSvcTest.java | 6 +- .../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 2 - .../ca/uhn/fhir/jpa/test/BaseJpaTest.java | 2 +- .../HapiFhirHibernateJpaDialectTest.java | 6 +- .../jpa/dao/index/ResourceVersionSvcTest.java | 6 +- .../jpa/delete/DeleteConflictServiceTest.java | 1 + .../jpa/term/LoincFullLoadR4SandboxIT.java | 10 +-- .../taskdef/ForceIdMigrationCopyTask.java | 71 ++++++++++++++++ .../fhir/jpa/migrate/tasks/api/Builder.java | 4 + 36 files changed, 281 insertions(+), 233 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml rename hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/{IForcedIdDaoImpl.java => IResourceTableDaoImpl.java} (75%) create mode 100644 hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml new file mode 100644 index 00000000000..c52021bb166 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 4803 +title: "Internal client-assigned ids are now resolved within the HFJ_RESOURCE table, avoiding a join to HFJ_FORCED_ID" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java index a926dc35c85..0339ea69546 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.system.HapiSystemProperties; import org.hibernate.HibernateException; @@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import org.springframework.orm.jpa.vendor.HibernateJpaDialect; +import javax.annotation.Nonnull; import javax.persistence.PersistenceException; import static org.apache.commons.lang3.StringUtils.defaultString; @@ -43,7 +44,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { private static final Logger ourLog = LoggerFactory.getLogger(HapiFhirHibernateJpaDialect.class); - private HapiLocalizer myLocalizer; + static final String RESOURCE_VERSION_CONSTRAINT_FAILURE = "resourceVersionConstraintFailure"; + private final HapiLocalizer myLocalizer; /** * Constructor @@ -61,7 +63,7 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { } @Override - protected DataAccessException convertHibernateAccessException(HibernateException theException) { + protected DataAccessException convertHibernateAccessException(@Nonnull HibernateException theException) { return convertHibernateAccessException(theException, null); } @@ -86,22 +88,17 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { if (isNotBlank(constraintName)) { constraintName = constraintName.toUpperCase(); if (constraintName.contains(ResourceHistoryTable.IDX_RESVER_ID_VER)) { - throw new ResourceVersionConflictException(Msg.code(823) - + messageToPrepend - + myLocalizer.getMessage( - HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure")); + throw new ResourceVersionConflictException( + Msg.code(823) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } if (constraintName.contains(ResourceIndexedComboStringUnique.IDX_IDXCMPSTRUNIQ_STRING)) { throw new ResourceVersionConflictException(Msg.code(824) - + messageToPrepend - + myLocalizer.getMessage( - HapiFhirHibernateJpaDialect.class, - "resourceIndexedCompositeStringUniqueConstraintFailure")); + + makeErrorMessage( + messageToPrepend, "resourceIndexedCompositeStringUniqueConstraintFailure")); } - if (constraintName.contains(ForcedId.IDX_FORCEDID_TYPE_FID)) { - throw new ResourceVersionConflictException(Msg.code(825) - + messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "forcedIdConstraintFailure")); + if (constraintName.contains(ResourceTable.IDX_RES_FHIR_ID)) { + throw new ResourceVersionConflictException( + Msg.code(825) + makeErrorMessage(messageToPrepend, "forcedIdConstraintFailure")); } if (constraintName.contains(ResourceSearchUrlEntity.RES_SEARCH_URL_COLUMN_NAME)) { throw super.convertHibernateAccessException(theException); @@ -124,21 +121,24 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { * StressTestR4Test method testMultiThreadedUpdateSameResourceInTransaction() */ if (theException instanceof org.hibernate.StaleStateException) { - String msg = messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure"); - throw new ResourceVersionConflictException(Msg.code(826) + msg); + throw new ResourceVersionConflictException( + Msg.code(826) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } if (theException instanceof org.hibernate.PessimisticLockException) { PessimisticLockException ex = (PessimisticLockException) theException; String sql = defaultString(ex.getSQL()).toUpperCase(); if (sql.contains(ResourceHistoryTable.HFJ_RES_VER)) { - String msg = messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure"); - throw new ResourceVersionConflictException(Msg.code(827) + msg); + throw new ResourceVersionConflictException( + Msg.code(827) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } } DataAccessException retVal = super.convertHibernateAccessException(theException); return retVal; } + + @Nonnull + private String makeErrorMessage(String thePrefix, String theMessageKey) { + return thePrefix + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, theMessageKey); + } } 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 af0cefa9854..ddde3bc3231 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 @@ -644,6 +644,7 @@ public abstract class BaseHapiFhirResourceDao extends B 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(); 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 index bd4a80f64ed..ed2425085bf 100644 --- 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 @@ -19,7 +19,6 @@ */ package ca.uhn.fhir.jpa.dao.data; -import ca.uhn.fhir.jpa.dao.data.custom.IForcedIdQueries; import ca.uhn.fhir.jpa.model.entity.ForcedId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -27,17 +26,16 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - +/** + * 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.7") @Repository -public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository, IForcedIdQueries { - - @Query("SELECT f FROM ForcedId f WHERE f.myResourcePid IN (:resource_pids)") - List findAllByResourcePid(@Param("resource_pids") List theResourcePids); - - @Query("SELECT f FROM ForcedId f WHERE f.myResourcePid = :resource_pid") - Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); +public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository { @Modifying @Query("DELETE FROM ForcedId t WHERE t.myId = :pid") 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 3696314341e..6bb08cbdc5e 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 @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.dao.data; +import ca.uhn.fhir.jpa.dao.data.custom.IForcedIdQueries; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -36,7 +37,8 @@ import java.util.Map; import java.util.Optional; @Transactional(propagation = Propagation.MANDATORY) -public interface IResourceTableDao extends JpaRepository, IHapiFhirJpaRepository { +public interface IResourceTableDao + extends JpaRepository, IHapiFhirJpaRepository, IForcedIdQueries { @Query("SELECT t.myId FROM ResourceTable t WHERE t.myDeleted IS NOT NULL") Slice findIdsOfDeletedResources(Pageable thePageable); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java similarity index 75% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java index 9f271a15df2..86e9977e890 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java @@ -27,9 +27,12 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Component -// Don't change the name of this class. Spring Data requires the name to match. -// See https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa -public class IForcedIdDaoImpl implements IForcedIdQueries { +/** + * Custom query implementations. + * Don't change the name of this class. Spring Data requires the name to match. + * https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa + */ +public class IResourceTableDaoImpl implements IForcedIdQueries { @PersistenceContext private EntityManager myEntityManager; @@ -51,11 +54,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoType( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )"; + String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id )"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -78,11 +79,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { Collection theForcedIds, Collection thePartitionId, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IN ( :partition_id )"; + String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IN ( :partition_id )"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -103,11 +102,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoTypeInPartitionNull( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IS NULL"; + String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IS NULL"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -130,11 +127,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { Collection theForcedIds, List thePartitionIdsWithoutDefault, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND (f.myPartitionIdValue IS NULL OR f.myPartitionIdValue IN ( :partition_id ))"; + String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN ( :partition_id ))"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index 3199edcdbaa..cee2c8bfd08 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -25,20 +25,17 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.cross.JpaResourceLookup; 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.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -104,9 +101,6 @@ public class IdHelperService implements IIdHelperService { public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0]; public static final String RESOURCE_PID = "RESOURCE_PID"; - @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired protected IResourceTableDao myResourceTableDao; @@ -270,6 +264,7 @@ public class IdHelperService implements IIdHelperService { * * @throws ResourceNotFoundException If the ID can not be found */ + @Nonnull @Override public JpaPid resolveResourcePersistentIds( @Nonnull RequestPartitionId theRequestPartitionId, @@ -327,7 +322,7 @@ public class IdHelperService implements IIdHelperService { @Override @Nonnull public List resolveResourcePersistentIdsWithCache( - RequestPartitionId theRequestPartitionId, List theIds, boolean theOnlyForcedIds) { + @Nonnull RequestPartitionId theRequestPartitionId, List theIds, boolean theOnlyForcedIds) { assert myDontCheckActiveTransactionForUnitTest || TransactionSynchronizationManager.isSynchronizationActive(); List retVal = new ArrayList<>(theIds.size()); @@ -375,20 +370,19 @@ public class IdHelperService implements IIdHelperService { RequestPartitionId theRequestPartitionId, List theIds, List theOutputListToPopulate) { CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = cb.createTupleQuery(); - Root from = criteriaQuery.from(ForcedId.class); + Root from = criteriaQuery.from(ResourceTable.class); /* - * We don't currently have an index that satisfies these three columns, but the - * index IDX_FORCEDID_TYPE_FID does include myResourceType and myForcedId - * so we're at least minimizing the amount of data we fetch. A largescale test - * on Postgres does confirm that this lookup does use the index and is pretty - * performant. + * IDX_RES_FHIR_ID covers these columns, but RES_ID is only INCLUDEd. + * Only PG, and MSSql support INCLUDE COLUMNS. + * @see AddIndexTask.generateSql */ criteriaQuery.multiselect( - from.get("myResourcePid").as(Long.class), + from.get("myId").as(Long.class), from.get("myResourceType").as(String.class), - from.get("myForcedId").as(String.class)); + from.get("myFhirId").as(String.class)); + // one create one clause per id. List predicates = new ArrayList<>(theIds.size()); for (IIdType next : theIds) { @@ -399,12 +393,13 @@ public class IdHelperService implements IIdHelperService { andPredicates.add(typeCriteria); } - Predicate idCriteria = cb.equal(from.get("myForcedId").as(String.class), next.getIdPart()); + Predicate idCriteria = cb.equal(from.get("myFhirId").as(String.class), next.getIdPart()); andPredicates.add(idCriteria); getOptionalPartitionPredicate(theRequestPartitionId, cb, from).ifPresent(andPredicates::add); predicates.add(cb.and(andPredicates.toArray(EMPTY_PREDICATE_ARRAY))); } + // join all the clauses as OR criteriaQuery.where(cb.or(predicates.toArray(EMPTY_PREDICATE_ARRAY))); TypedQuery query = myEntityManager.createQuery(criteriaQuery); @@ -432,7 +427,7 @@ public class IdHelperService implements IIdHelperService { * 3. If the requested partition search is not all partition, return the request partition as predicate. */ private Optional getOptionalPartitionPredicate( - RequestPartitionId theRequestPartitionId, CriteriaBuilder cb, Root from) { + RequestPartitionId theRequestPartitionId, CriteriaBuilder cb, Root from) { if (myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()) { return Optional.empty(); } else if (theRequestPartitionId.isDefaultPartition() && myPartitionSettings.getDefaultPartitionId() == null) { @@ -488,7 +483,7 @@ public class IdHelperService implements IIdHelperService { return myMemoryCacheService.get( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theId.getId(), - pid -> myForcedIdDao.findByResourcePid(pid).map(ForcedId::asTypedFhirResourceId)); + pid -> myResourceTableDao.findById(pid).map(ResourceTable::asTypedFhirResourceId)); } private ListMultimap organizeIdsByResourceType(Collection theIds) { @@ -538,37 +533,35 @@ public class IdHelperService implements IIdHelperService { for (Iterator forcedIdIterator = nextIds.iterator(); forcedIdIterator.hasNext(); ) { String nextForcedId = forcedIdIterator.next(); String nextKey = nextResourceType + "/" + nextForcedId; - IResourceLookup cachedLookup = + IResourceLookup cachedLookup = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey); if (cachedLookup != null) { forcedIdIterator.remove(); - if (!retVal.containsKey(nextForcedId)) { - retVal.put(nextForcedId, new ArrayList<>()); - } - retVal.get(nextForcedId).add(cachedLookup); + retVal.computeIfAbsent(nextForcedId, id -> new ArrayList<>()) + .add(cachedLookup); } } } - if (nextIds.size() > 0) { + if (!nextIds.isEmpty()) { Collection views; assert isNotBlank(nextResourceType); if (requestPartitionId.isAllPartitions()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoType( + views = myResourceTableDao.findAndResolveByForcedIdWithNoType( nextResourceType, nextIds, theExcludeDeleted); } else { if (requestPartitionId.isDefaultPartition()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( nextResourceType, nextIds, theExcludeDeleted); } else if (requestPartitionId.hasDefaultPartitionId()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( nextResourceType, nextIds, requestPartitionId.getPartitionIdsWithoutDefault(), theExcludeDeleted); } else { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( nextResourceType, nextIds, requestPartitionId.getPartitionIds(), theExcludeDeleted); } } @@ -580,10 +573,7 @@ public class IdHelperService implements IIdHelperService { Date deletedAt = (Date) next[3]; JpaResourceLookup lookup = new JpaResourceLookup(resourceType, resourcePid, deletedAt); - if (!retVal.containsKey(forcedId)) { - retVal.put(forcedId, new ArrayList<>()); - } - retVal.get(forcedId).add(lookup); + retVal.computeIfAbsent(forcedId, id -> new ArrayList<>()).add(lookup); if (!myStorageSettings.isDeleteEnabled()) { String key = resourceType + "/" + forcedId; @@ -616,19 +606,16 @@ public class IdHelperService implements IIdHelperService { for (Iterator forcedIdIterator = thePidsToResolve.iterator(); forcedIdIterator.hasNext(); ) { Long nextPid = forcedIdIterator.next(); String nextKey = Long.toString(nextPid); - IResourceLookup cachedLookup = + IResourceLookup cachedLookup = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey); if (cachedLookup != null) { forcedIdIterator.remove(); - if (!theTargets.containsKey(nextKey)) { - theTargets.put(nextKey, new ArrayList<>()); - } - theTargets.get(nextKey).add(cachedLookup); + theTargets.computeIfAbsent(nextKey, id -> new ArrayList<>()).add(cachedLookup); } } } - if (thePidsToResolve.size() > 0) { + if (!thePidsToResolve.isEmpty()) { Collection lookup; if (theRequestPartitionId.isAllPartitions()) { lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve); @@ -661,7 +648,7 @@ public class IdHelperService implements IIdHelperService { } @Override - public PersistentIdToForcedIdMap translatePidsToForcedIds(Set theResourceIds) { + public PersistentIdToForcedIdMap translatePidsToForcedIds(Set theResourceIds) { assert myDontCheckActiveTransactionForUnitTest || TransactionSynchronizationManager.isSynchronizationActive(); Set thePids = theResourceIds.stream().map(JpaPid::getId).collect(Collectors.toSet()); Map> retVal = new HashMap<>( @@ -671,11 +658,11 @@ public class IdHelperService implements IIdHelperService { thePids.stream().filter(t -> !retVal.containsKey(t)).collect(Collectors.toList()); new QueryChunker().chunk(remainingPids, t -> { - List forcedIds = myForcedIdDao.findAllByResourcePid(t); + List resourceEntities = myResourceTableDao.findAllById(t); - for (ForcedId forcedId : forcedIds) { - Long nextResourcePid = forcedId.getResourceId(); - Optional nextForcedId = Optional.of(forcedId.asTypedFhirResourceId()); + for (ResourceTable nextResourceEntity : resourceEntities) { + Long nextResourcePid = nextResourceEntity.getId(); + Optional nextForcedId = Optional.of(nextResourceEntity.asTypedFhirResourceId()); retVal.put(nextResourcePid, nextForcedId); myMemoryCacheService.putAfterCommit( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, nextForcedId); @@ -688,11 +675,11 @@ public class IdHelperService implements IIdHelperService { myMemoryCacheService.putAfterCommit( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, Optional.empty()); } - Map> convertRetVal = new HashMap<>(); + Map> convertRetVal = new HashMap<>(); retVal.forEach((k, v) -> { convertRetVal.put(JpaPid.fromId(k), v); }); - return new PersistentIdToForcedIdMap(convertRetVal); + return new PersistentIdToForcedIdMap<>(convertRetVal); } /** @@ -799,7 +786,7 @@ public class IdHelperService implements IIdHelperService { @Override public IIdType resourceIdFromPidOrThrowException(JpaPid thePid, String theResourceType) { Optional optionalResource = myResourceTableDao.findById(thePid.getId()); - if (!optionalResource.isPresent()) { + if (optionalResource.isEmpty()) { throw new ResourceNotFoundException(Msg.code(2124) + "Requested resource not found"); } return optionalResource.get().getIdDt().toVersionless(); @@ -820,7 +807,7 @@ public class IdHelperService implements IIdHelperService { public Set translatePidsToFhirResourceIds(Set thePids) { assert TransactionSynchronizationManager.isSynchronizationActive(); - PersistentIdToForcedIdMap pidToForcedIdMap = translatePidsToForcedIds(thePids); + PersistentIdToForcedIdMap pidToForcedIdMap = translatePidsToForcedIds(thePids); return pidToForcedIdMap.getResolvedResourceIds(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java index a257166230e..96ee1f1f8cc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.jpa.entity; -import ca.uhn.fhir.jpa.model.entity.ForcedId; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import java.io.Serializable; import javax.persistence.Column; @@ -60,7 +60,7 @@ public class BulkExportCollectionFileEntity implements Serializable { foreignKey = @ForeignKey(name = "FK_BLKEXCOLFILE_COLLECT")) private BulkExportCollectionEntity myCollection; - @Column(name = "RES_ID", length = ForcedId.MAX_FORCED_ID_LENGTH, nullable = false) + @Column(name = "RES_ID", length = ResourceTable.MAX_FORCED_ID_LENGTH, nullable = false) private String myResourceId; public void setCollection(BulkExportCollectionEntity theCollection) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java index 3c5b9a1687b..830a7ff209a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceSearchView.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.api.Constants; @@ -46,13 +46,14 @@ import javax.persistence.TemporalType; @SuppressWarnings("SqlDialectInspection") @Entity @Immutable -@Subselect("SELECT h.pid as pid, " + " r.res_id as res_id, " +@Subselect("SELECT h.pid as pid, " + + " r.res_id as res_id, " + " h.res_type as res_type, " + " h.res_version as res_version, " - + // FHIR version - " h.res_ver as res_ver, " - + // resource version - " h.has_tags as has_tags, " + // FHIR version + + " h.res_ver as res_ver, " + // resource version + + " h.has_tags as has_tags, " + " h.res_deleted_at as res_deleted_at, " + " h.res_published as res_published, " + " h.res_updated as res_updated, " @@ -62,11 +63,10 @@ import javax.persistence.TemporalType; + " h.PARTITION_ID as PARTITION_ID, " + " p.SOURCE_URI as PROV_SOURCE_URI," + " p.REQUEST_ID as PROV_REQUEST_ID," - + " f.forced_id as FORCED_PID " - + "FROM HFJ_RES_VER h " - + " LEFT OUTER JOIN HFJ_FORCED_ID f ON f.resource_pid = h.res_id " - + " LEFT OUTER JOIN HFJ_RES_VER_PROV p ON p.res_ver_pid = h.pid " - + " INNER JOIN HFJ_RESOURCE r ON r.res_id = h.res_id and r.res_ver = h.res_ver") + + " r.fhir_id as FHIR_ID " + + "FROM HFJ_RESOURCE r " + + " INNER JOIN HFJ_RES_VER h ON r.res_id = h.res_id and r.res_ver = h.res_ver" + + " LEFT OUTER JOIN HFJ_RES_VER_PROV p ON p.res_ver_pid = h.pid ") public class ResourceSearchView implements IBaseResourceEntity, Serializable { private static final long serialVersionUID = 1L; @@ -120,13 +120,15 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable { @Enumerated(EnumType.STRING) private ResourceEncodingEnum myEncoding; - @Column(name = "FORCED_PID", length = ForcedId.MAX_FORCED_ID_LENGTH) - private String myForcedPid; + @Column(name = "FHIR_ID", length = ResourceTable.MAX_FORCED_ID_LENGTH) + private String myFhirId; @Column(name = "PARTITION_ID") private Integer myPartitionId; - public ResourceSearchView() {} + public ResourceSearchView() { + // public constructor for Hibernate + } public String getResourceTextVc() { return myResourceTextVc; @@ -158,8 +160,8 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable { myFhirVersion = theFhirVersion; } - public String getForcedId() { - return myForcedPid; + public String getFhirId() { + return myFhirId; } @Override @@ -169,12 +171,11 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable { @Override public IdDt getIdDt() { - if (myForcedPid == null) { + if (myFhirId == null) { Long id = myResourceId; return new IdDt(myResourceType + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } else { - return new IdDt( - getResourceType() + '/' + getForcedId() + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + return new IdDt(getResourceType() + '/' + getFhirId() + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index c17de307498..444549e8968 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateOrdinalDatesTask; import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; +import ca.uhn.fhir.jpa.migrate.taskdef.ForceIdMigrationCopyTask; import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; import ca.uhn.fhir.jpa.model.config.PartitionSettings; @@ -110,6 +111,19 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .addIndex("20230911.2", "IDX_EMPi_TGT_MR_SCore") .unique(false) .withColumns("TARGET_TYPE", "MATCH_RESULT", "SCORE"); + + // Move forced_id constraints to hfj_resource and the new fhir_id column + // Note: we leave the HFJ_FORCED_ID.IDX_FORCEDID_TYPE_FID index in place to support old writers for a while. + version.addTask(new ForceIdMigrationCopyTask(version.getRelease(), "20231018.1")); + + Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); + hfjResource.modifyColumn("20231018.2", "FHIR_ID").nonNullable(); + hfjResource + .addIndex("20231018.3", "IDX_RES_FHIR_ID") + .unique(true) + .online(true) + .includeColumns("RES_ID") + .withColumns("FHIR_ID", "RES_TYPE"); } protected void init680() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java index 2982ab20296..9891672a00f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java @@ -23,7 +23,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import java.util.Date; -public class JpaResourceLookup implements IResourceLookup { +public class JpaResourceLookup implements IResourceLookup { private final String myResourceType; private final Long myResourcePid; private final Date myDeletedAt; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java index ada96b93ad7..bd1de11154e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java @@ -25,11 +25,9 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; 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.IResourceTableDao; 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.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -38,8 +36,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import static org.apache.commons.lang3.StringUtils.isBlank; - /** * @deprecated */ @@ -50,9 +46,6 @@ public class ResourceReindexer { @Autowired private IResourceHistoryTableDao myResourceHistoryTableDao; - @Autowired - private IForcedIdDao myForcedIdDao; - @Autowired private IResourceTableDao myResourceTableDao; @@ -75,21 +68,6 @@ public class ResourceReindexer { } public void reindexResourceEntity(ResourceTable theResourceTable) { - /* - * This part is because from HAPI 1.5 - 1.6 we changed the format of forced ID to be "type/id" instead of just "id" - */ - ForcedId forcedId = theResourceTable.getForcedId(); - if (forcedId != null) { - if (isBlank(forcedId.getResourceType())) { - ourLog.info( - "Updating resource {} forcedId type to {}", - forcedId.getForcedId(), - theResourceTable.getResourceType()); - forcedId.setResourceType(theResourceTable.getResourceType()); - myForcedIdDao.save(forcedId); - } - } - IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceTable.getResourceType()); long expectedVersion = theResourceTable.getVersion(); IBaseResource resource = dao.readByPid(JpaPid.fromId(theResourceTable.getId()), true); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java index 38243681312..064e193bfad 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity; @@ -111,9 +110,6 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc, IHasSc @Autowired private DaoRegistry myDaoRegistry; - @Autowired - private IForcedIdDao myForcedIdDao; - @Autowired private FhirContext myContext; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 615c6fe57df..edc52f2e4a0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -61,7 +61,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; 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.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs; @@ -2972,14 +2971,15 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { public Optional readCodeSystemByForcedId(String theForcedId) { @SuppressWarnings("unchecked") List resultList = (List) myEntityManager - .createQuery("select f.myResource from ForcedId f " - + "where f.myResourceType = 'CodeSystem' and f.myForcedId = '" + theForcedId + "'") + .createQuery("select r from ResourceTable r " + + "where r.myResourceType = 'CodeSystem' and r.myFhirId = :fhirId") + .setParameter("fhirId", theForcedId) .getResultList(); if (resultList.isEmpty()) return Optional.empty(); if (resultList.size() > 1) throw new NonUniqueResultException(Msg.code(911) + "More than one CodeSystem is pointed by forcedId: " - + theForcedId + ". Was constraint " + ForcedId.IDX_FORCEDID_TYPE_FID + " removed?"); + + theForcedId + ". Was constraint " + ResourceTable.IDX_RES_FHIR_ID + " removed?"); IFhirResourceDao csDao = myDaoRegistry.getResourceDao("CodeSystem"); IBaseResource cs = myJpaStorageResourceParser.toResource(resultList.get(0), false); 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 ad3205f2795..245abc493d9 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 @@ -70,6 +70,7 @@ public abstract class BaseHasResource extends BasePartitionable * after an update */ @Transient + // TODO MB forced_id delete this in step 3 private transient String myTransientForcedId; public String getTransientForcedId() { 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 7046e8c5c3e..c0aab1eb5b7 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 @@ -64,7 +64,6 @@ import javax.persistence.UniqueConstraint; */ @Index(name = "IDX_FORCEID_FID", columnList = "FORCED_ID"), // @Index(name = "IDX_FORCEID_RESID", columnList = "RESOURCE_PID"), - // TODO GGG potentiall add a type + res_id index here, specifically for deletion? }) public class ForcedId extends BasePartitionable { 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 784ad91ae5f..ce53e3bb841 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 @@ -277,12 +277,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl if (getTransientForcedId() != null) { resourceIdPart = getTransientForcedId(); } else { - if (getResourceTable().getForcedId() == null) { - Long id = getResourceId(); - resourceIdPart = id.toString(); - } else { - resourceIdPart = getResourceTable().getForcedId().getForcedId(); - } + resourceIdPart = getResourceTable().getFhirId(); } return new IdDt(getResourceType() + '/' + resourceIdPart + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } 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 b3bc742b248..f516209a3cd 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 @@ -73,20 +73,27 @@ import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.Table; import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; import javax.persistence.Version; +import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_FHIR_ID; + @Indexed(routingBinder = @RoutingBinderRef(type = ResourceTableRoutingBinder.class)) @Entity @Table( name = ResourceTable.HFJ_RESOURCE, - uniqueConstraints = {}, + uniqueConstraints = { + @UniqueConstraint( + name = IDX_RES_FHIR_ID, + columnNames = {"FHIR_ID", "RES_TYPE"}) + }, indexes = { // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE @Index(name = "IDX_RES_DATE", columnList = BaseHasResource.RES_UPDATED), @Index( name = "IDX_RES_TYPE_DEL_UPDATED", columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"), - @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID,RES_UPDATED,PARTITION_ID"), + @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID, RES_UPDATED, PARTITION_ID") }) @NamedEntityGraph(name = "Resource.noJoins") public class ResourceTable extends BaseHasResource implements Serializable, IBasePersistedResource { @@ -95,6 +102,9 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas public static final String RES_TYPE = "RES_TYPE"; private static final int MAX_LANGUAGE_LENGTH = 20; private static final long serialVersionUID = 1L; + public static final int MAX_FORCED_ID_LENGTH = 100; + public static final String IDX_RES_FHIR_ID = "IDX_RES_FHIR_ID"; + /** * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB * Note the extra config needed in HS6 for indexing transient props: @@ -982,24 +992,18 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas } private void populateId(IIdType retVal) { + String resourceId; if (myFhirId != null && !myFhirId.isEmpty()) { - retVal.setValue(getResourceType() + '/' + myFhirId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + resourceId = myFhirId; } else if (getTransientForcedId() != null) { - // Avoid a join query if possible - retVal.setValue(getResourceType() - + '/' - + getTransientForcedId() - + '/' - + Constants.PARAM_HISTORY - + '/' - + getVersion()); - } else if (getForcedId() == null) { - Long id = this.getResourceId(); - retVal.setValue(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + resourceId = getTransientForcedId(); + } else if (myForcedId != null) { + resourceId = myForcedId.getForcedId(); } else { - String forcedId = getForcedId().getForcedId(); - retVal.setValue(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + Long id = this.getResourceId(); + resourceId = Long.toString(id); } + retVal.setValue(getResourceType() + '/' + resourceId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } public String getCreatedByMatchUrl() { @@ -1047,6 +1051,10 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas myFhirId = theFhirId; } + public String asTypedFhirResourceId() { + return getResourceType() + "/" + getFhirId(); + } + /** * Populate myFhirId with server-assigned sequence id when no client-id provided. * We eat this complexity during insert to simplify query time with a uniform column. 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 c170a18de47..a5ccb698720 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 @@ -43,6 +43,6 @@ public class ResourceTableTest { IdDt actual = t.getIdDt(); // Then - assertTrue(actual.equals(theExpected)); + assertEquals(theExpected, actual.getValueAsString()); } } 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 f62de15e9cf..184a18fb8aa 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 @@ -44,7 +44,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ClasspathUtil; import com.google.common.collect.Lists; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; @@ -104,7 +103,6 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -632,7 +630,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { if (readBackResource.getForcedId() != null) { assertEquals(myExpectedId, readBackResource.getForcedId().getForcedId(), "legacy join populated"); - assertEquals(myExpectedId, readBackView.getForcedId(), + assertEquals(myExpectedId, readBackView.getFhirId(), "legacy join populated"); } else { assertEquals(IdStrategyEnum.SEQUENTIAL_NUMERIC, theServerIdStrategy, 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 58562b52960..c005497f6af 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 @@ -190,8 +190,8 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), - either(containsString("forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='B' and (forcedid0_.PARTITION_ID is null) or forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='A' and (forcedid0_.PARTITION_ID is null)")) - .or(containsString("forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='A' and (forcedid0_.PARTITION_ID is null) or forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='B' and (forcedid0_.PARTITION_ID is null)"))); + either(containsString("resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='B' and (resourceta0_.PARTITION_ID is null) or resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='A' and (resourceta0_.PARTITION_ID is null)")) + .or(containsString("resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='A' and (resourceta0_.PARTITION_ID is null) or resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='B' and (resourceta0_.PARTITION_ID is null)"))); assertEquals(52, 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/dao/index/IdHelperServiceTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java index fd986e6745b..7401eab2982 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.dao.index; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.dao.JpaPid; @@ -41,7 +41,7 @@ public class IdHelperServiceTest { private JpaStorageSettings myStorageSettings; @Mock - private IForcedIdDao myForcedIdDao; + private IResourceTableDao myResourceTableDao; @Mock private MemoryCacheService myMemoryCacheService; @@ -100,7 +100,7 @@ public class IdHelperServiceTest { // when when(myStorageSettings.isDeleteEnabled()) .thenReturn(true); - when(myForcedIdDao.findAndResolveByForcedIdWithNoType(Mockito.anyString(), + when(myResourceTableDao.findAndResolveByForcedIdWithNoType(Mockito.anyString(), Mockito.anyList(), Mockito.anyBoolean())) .thenReturn(Collections.singletonList(redView)) .thenReturn(Collections.singletonList(blueView)); @@ -164,7 +164,7 @@ public class IdHelperServiceTest { Collection testForcedIdViews = new ArrayList<>(); testForcedIdViews.add(forcedIdView); - when(myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(any(), any(), any(), anyBoolean())).thenReturn(testForcedIdViews); + when(myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition(any(), any(), any(), anyBoolean())).thenReturn(testForcedIdViews); IResourceLookup result = myHelperService.resolveResourceIdentity(partitionId, resourceType, resourceForcedId); assertEquals(forcedIdView[0], result.getResourceType()); 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 d37069791dc..2700b1b2489 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 @@ -8,6 +8,7 @@ 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; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @@ -137,6 +138,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_FHIR_ID).executeUpdate(); }); myHaveDroppedForcedIdUniqueConstraint = true; } 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 44828cd39ab..3972c959dda 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 @@ -853,8 +853,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { myCaptureQueriesListener.logSelectQueriesForCurrentThread(); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.resource_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.forced_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.fhir_id in ('a')"), selectQuery); selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "select t1.res_id from hfj_resource t1"), selectQuery); @@ -895,8 +895,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.resource_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.forced_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.fhir_id in ('a')"), selectQuery); } // Search by ID where at least one ID is a numeric ID @@ -1504,8 +1504,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { // Forced ID resolution resultingQueryNotFormatted = queries.get(0); - assertThat(resultingQueryNotFormatted, containsString("RESOURCE_TYPE='Organization'")); - assertThat(resultingQueryNotFormatted, containsString("forcedid0_.RESOURCE_TYPE='Organization' and forcedid0_.FORCED_ID='ORG1' or forcedid0_.RESOURCE_TYPE='Organization' and forcedid0_.FORCED_ID='ORG2'")); + assertThat(resultingQueryNotFormatted, containsString("RES_TYPE='Organization'")); + assertThat(resultingQueryNotFormatted, containsString("resourceta0_.RES_TYPE='Organization' and resourceta0_.FHIR_ID='ORG1' or resourceta0_.RES_TYPE='Organization' and resourceta0_.FHIR_ID='ORG2'")); // The search itself resultingQueryNotFormatted = queries.get(1); 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 f760890030a..75eb2f79d01 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 @@ -181,7 +181,7 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertTrue(patient.getActive()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("forcedid0_.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("resourceta0_.PARTITION_ID in (?)")); assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("where resourceta0_.PARTITION_ID=? and resourceta0_.RES_ID=?")); } @@ -229,7 +229,7 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertEquals(1, outcome.size()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("forcedid0_.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("resourceta0_.PARTITION_ID in (?)")); assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("t0.PARTITION_ID = ?")); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 11cd5638c18..5c5e356a283 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -181,7 +181,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( "Patient", Arrays.asList(patientId) ); assertContainsSingleForcedId(forcedIds, patientId); @@ -189,7 +189,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoType( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoType( "Patient", Arrays.asList(patientId), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -200,7 +200,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( "Patient", Arrays.asList(patientId) ); assertContainsSingleForcedId(forcedIds, patientId); @@ -208,7 +208,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoType( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoType( "Patient", Arrays.asList(patientId), true ); assertThat(forcedIds, hasSize(0)); @@ -223,7 +223,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -231,7 +231,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -242,7 +242,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -250,7 +250,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), true ); assertEquals(0, forcedIds.size()); @@ -267,7 +267,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -275,7 +275,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -285,7 +285,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -293,7 +293,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), true ); assertEquals(0, forcedIds.size()); @@ -310,7 +310,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -318,7 +318,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -328,7 +328,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -336,7 +336,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), true ); assertEquals(0, forcedIds.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java index 4171540f847..b01715e9b27 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity; @@ -61,8 +60,6 @@ public class ResourceReindexingSvcImplTest { @Mock private DaoRegistry myDaoRegistry; @Mock - private IForcedIdDao myForcedIdDao; - @Mock private IResourceReindexJobDao myReindexJobDao; @Mock private IResourceTableDao myResourceTableDao; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java index a9610ed7f54..43dd37135fb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java @@ -224,7 +224,7 @@ class ITermReadSvcTest { @Test void getNoneReturnsOptionalEmpty() { - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Collections.emptyList()); Optional result = testedClass.readCodeSystemByForcedId("a-cs-id"); @@ -233,7 +233,7 @@ class ITermReadSvcTest { @Test void getMultipleThrows() { - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Lists.newArrayList(resource1, resource2)); NonUniqueResultException thrown = assertThrows( @@ -247,7 +247,7 @@ class ITermReadSvcTest { void getOneConvertToResource() { ReflectionTestUtils.setField(testedClass, "myDaoRegistry", myDaoRegistry); - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Lists.newArrayList(resource1)); when(myDaoRegistry.getResourceDao("CodeSystem")).thenReturn(myFhirResourceDao); when(myJpaStorageResourceParser.toResource(resource1, false)).thenReturn(myCodeSystemResource); 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 920285cf3ff..7bf94fc8e07 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 @@ -425,8 +425,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao; @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired @Qualifier("myCoverageDaoR4") protected IFhirResourceDao myCoverageDao; @Autowired 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 dd44fea8b2c..79019e41f28 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 @@ -257,7 +257,7 @@ public abstract class BaseJpaTest extends BaseTest { @Autowired private IResourceHistoryTableDao myResourceHistoryTableDao; @Autowired - private IForcedIdDao myForcedIdDao; + protected IForcedIdDao myForcedIdDao; @Autowired private DaoRegistry myDaoRegistry; private final List myRegisteredInterceptors = new ArrayList<>(1); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java index 5b942fdd69b..b0bafdcdf7f 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java @@ -1,8 +1,8 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.i18n.HapiLocalizer; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import org.hibernate.HibernateException; import org.hibernate.PersistentObjectException; @@ -36,7 +36,7 @@ public class HapiFhirHibernateJpaDialectTest { assertThat(outcome.getMessage(), containsString("this is a message")); try { - mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ForcedId.IDX_FORCEDID_TYPE_FID)); + mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_FHIR_ID)); fail(); } catch (ResourceVersionConflictException e) { assertThat(e.getMessage(), containsString("The operation has failed with a client-assigned ID constraint failure")); @@ -67,7 +67,7 @@ public class HapiFhirHibernateJpaDialectTest { assertEquals("FOO", outcome.getMessage()); try { - PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ForcedId.IDX_FORCEDID_TYPE_FID)); + PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_FHIR_ID)); mySvc.translate(exception, "a message"); fail(); } catch (ResourceVersionConflictException e) { diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java index 9c37bce5cf0..1d55666e468 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; 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 org.hl7.fhir.instance.model.api.IIdType; @@ -33,6 +34,7 @@ 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; @@ -73,8 +75,8 @@ public class ResourceVersionSvcTest { */ private void mock_resolveResourcePersistentIdsWithCache_toReturnNothing() { CriteriaBuilder cb = Mockito.mock(CriteriaBuilder.class); - CriteriaQuery criteriaQuery = Mockito.mock(CriteriaQuery.class); - Root from = Mockito.mock(Root.class); + 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/delete/DeleteConflictServiceTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java index f9a70508c29..117deb50a97 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java @@ -50,6 +50,7 @@ public class DeleteConflictServiceTest { @Test public void noInterceptorTwoConflictsDoesntRetry() { ResourceTable entity = new ResourceTable(); + entity.setId(22L); DeleteConflictList deleteConflicts = new DeleteConflictList(); List list = new ArrayList<>(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java index 67ad68cba3b..dd4f999f059 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java @@ -11,7 +11,6 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptProperty; import ca.uhn.fhir.jpa.entity.TermValueSet; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; @@ -599,12 +598,9 @@ public class LoincFullLoadR4SandboxIT extends BaseJpaTest { */ private void queryForSpecificValueSet() { runInTransaction(() -> { - Query q = myEntityManager.createQuery("from ForcedId where myForcedId like 'LG8749-6%'"); - @SuppressWarnings("unchecked") - List fIds = (List) q.getResultList(); - long res_id = fIds.stream().map(ForcedId::getId).sorted().findFirst().orElse(fail("ForcedId not found")); - - Query q1 = myEntityManager.createQuery("from ResourceTable where id = " + res_id); + Query q1 = myEntityManager + .createQuery("from ResourceTable where myFhirId like :fhir_id") + .setParameter("fhir_id", "LG8749-6%"); @SuppressWarnings("unchecked") List vsList = (List) q1.getResultList(); assertEquals(1, vsList.size()); diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java new file mode 100644 index 00000000000..9e95993c7fc --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java @@ -0,0 +1,71 @@ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.sql.SQLException; + +public class ForceIdMigrationCopyTask extends BaseTask { + private static final Logger ourLog = LoggerFactory.getLogger(ForceIdMigrationCopyTask.class); + + public ForceIdMigrationCopyTask(String theProductVersion, String theSchemaVersion) { + super(theProductVersion, theSchemaVersion); + } + + @Override + public void validate() { + // no-op + } + + @Override + protected void doExecute() throws SQLException { + logInfo(ourLog, "Starting: migrate fhir_id from hfj_forced_id to hfj_resource.fhir_id"); + + JdbcTemplate jdbcTemplate = newJdbcTemplate(); + + Pair range = jdbcTemplate.queryForObject( + "select min(RES_ID), max(RES_ID) from HFJ_RESOURCE", + (rs, rowNum) -> Pair.of(rs.getLong(1), rs.getLong(2))); + + if (range == null || range.getLeft() == null) { + logInfo(ourLog, "HFJ_RESOURCE is empty. No work to do."); + return; + } + + // run update in batches. + int rowsPerBlock = 50; // hfj_resource has roughly 50 rows per 8k block. + int batchSize = rowsPerBlock * 2000; // a few thousand IOPS gives a batch size around a second. + for (long batchStart = range.getLeft(); batchStart <= range.getRight(); batchStart = batchStart + batchSize) { + long batchEnd = batchStart + batchSize; + ourLog.info("Migrating client-assigned ids for pids: {}-{}", batchStart, batchEnd); + + // This should be fast-ish since fhir_id isn't indexed yet, + // and we're walking both hfj_resource and hfj_forced_id in insertion order. + executeSql( + "hfj_resource", + "update hfj_resource " + "set fhir_id = coalesce( " + + // use first non-null value: forced_id if present, otherwise res_id + " (select f.forced_id from hfj_forced_id f where f.resource_pid = res_id), " + + " cast(res_id as char(64)) " + + " ) " + + "where fhir_id is null " + + "and res_id >= ? and res_id < ?", + batchStart, + batchEnd); + } + } + + @Override + protected void generateHashCode(HashCodeBuilder theBuilder) { + // no-op - this is a singleton. + } + + @Override + protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { + // no-op - this is a singleton. + } +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index ad830f51f11..138743a2864 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -633,4 +633,8 @@ public class Builder { return this; } } + + public String getRelease() { + return myRelease; + } } From 953f73529adff95df15c579d5f69cab314657450 Mon Sep 17 00:00:00 2001 From: michaelabuckley Date: Fri, 20 Oct 2023 13:26:24 -0400 Subject: [PATCH 20/86] Review changes for #4803 (#5381) review changes --- .../main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java | 2 +- .../fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 index ed2425085bf..3042da6b639 100644 --- 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 @@ -33,7 +33,7 @@ import org.springframework.stereotype.Repository; * No runtime code should query this table except for deletions by PK. * To be deleted in 2024 (zero-downtime). */ -@Deprecated(since = "6.7") +@Deprecated(since = "6.10") @Repository public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java index 86e9977e890..a8d86d69e43 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java @@ -54,7 +54,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoType( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + "FROM ResourceTable t " + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id )"; @@ -79,7 +79,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { Collection theForcedIds, Collection thePartitionId, boolean theExcludeDeleted) { - String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + "FROM ResourceTable t " + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IN ( :partition_id )"; @@ -102,7 +102,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoTypeInPartitionNull( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + "FROM ResourceTable t " + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IS NULL"; @@ -127,7 +127,7 @@ public class IResourceTableDaoImpl implements IForcedIdQueries { Collection theForcedIds, List thePartitionIdsWithoutDefault, boolean theExcludeDeleted) { - String query = "SELECT t.myResourceType, t.id, t.myFhirId, t.myDeleted " + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + "FROM ResourceTable t " + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN ( :partition_id ))"; From 1776a8a39594dd77af3311bcbc02e34d5d3f0f0d Mon Sep 17 00:00:00 2001 From: Tadgh Date: Sat, 21 Oct 2023 09:35:21 -0700 Subject: [PATCH 21/86] Bump version (#5386) --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 ++-- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- .../hapi/fhir/changelog/6_10_0/version.yaml | 2 +- .../hapi/fhir/changelog/7_0_0/changes.yaml | 0 .../uhn/hapi/fhir/changelog/7_0_0/upgrade.md | 0 .../hapi/fhir/changelog/7_0_0/version.yaml | 3 +++ hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 ++-- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- .../taskdef/ForceIdMigrationCopyTask.java | 19 +++++++++++++++++++ hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 82 files changed, 102 insertions(+), 80 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index d0dee2ba288..34d7898f84c 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 8fca23316db..3415ba0b4fd 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 74170e83cf3..6411069d373 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index a3a762e6942..c3f812a01bd 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 1c5f54d6dae..37a6009c2b0 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-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 cdf8bfd4252..92fa2dc2346 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 4a2718254e6..d9b77e0fae5 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index e90ff04faef..386cda0f5f4 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 54405842ff1..a632a1bed98 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 7ccf7bcadc3..21ccd151f43 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index cfdfed5e12d..2e46ecadfba 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index e6503bfc300..04ecb9a4bf6 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 5e69ddf10f4..16d49e4858d 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml index d7d82eaec5e..f8715a7ba72 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml @@ -1,3 +1,3 @@ --- release-date: "2023-11-18" -codename: "TBD" +codename: "Zed" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml new file mode 100644 index 00000000000..08d30a1acef --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-02-18" +codename: "TBD" diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 1a91d9252e9..fe201071976 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 115ce1185e2..168fad35745 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 9323628d70b..0a404648b65 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 8b5b0bb7239..56693416e0a 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 9ea06aa38fb..c336374b3eb 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 8a86baff174..8104243a146 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 27cd43e8e83..84849832b0f 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index d9b8c991f08..74119cc15e0 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 1c107b413b0..1e3cd6500a1 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 95cb771b09a..985f107995b 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index ff5c808ea66..3c7c5a5401b 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 - 6.9.10-SNAPSHOT + 6.11.0-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 92d2352b9ae..8fc1cc0cb54 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 5cd55a517fa..ba294c354e2 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index a854824c9a8..141e3c3ec76 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 83ef4b6dc18..b0e6eb43e37 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 50cd37ca724..940538b889d 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 1bc77fdb078..2a647677748 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 214c9b20a14..04854c03ab5 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 976f53f4085..61f9a18c007 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index a5633255950..843717757f7 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index b901a4bb13c..c50340dc1a6 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 7054dfb2c21..1f4e3686315 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index cfd1bc822de..5971966a2ef 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 - 6.9.10-SNAPSHOT + 6.11.0-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 0f6550cef86..63db8746c57 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 036155a7e47..02aa7a66f9b 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 - 6.9.10-SNAPSHOT + 6.11.0-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 2a25920f44e..3755df79214 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 0a009891136..bba7b7c6c9c 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.10-SNAPSHOT + 6.11.0-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 3d44dafc894..4e50b66568c 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 - 6.9.10-SNAPSHOT + 6.11.0-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 9e3c5a056bf..a05240a022f 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 - 6.9.10-SNAPSHOT + 6.11.0-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 091ec2fe405..94495f02513 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 - 6.9.10-SNAPSHOT + 6.11.0-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 aff764c6c2d..f69a1d9c9a7 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 - 6.9.10-SNAPSHOT + 6.11.0-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 e19bcae6932..24b0b2136c1 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 - 6.9.10-SNAPSHOT + 6.11.0-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 5e4a7670ff7..332a8c31eae 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index bea999f9baa..c5dc0e3d5d3 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 44cbfd4db96..d76ab3688d6 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java index 9e95993c7fc..bdf1030c639 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2023 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.migrate.taskdef; import org.apache.commons.lang3.builder.EqualsBuilder; diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 0b710f29008..21c0d51fce4 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index f610cd6e8a2..fa949045515 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index e878ac62fbf..77273b31422 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 780db0b63f8..2ea85ebb887 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index e378a7898cb..0c3d42fc4cc 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index ed87536639b..5601fd23345 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 9a89e84a27a..d404c922d58 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-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 409096bfece..a39eace08ee 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 7f487527d73..c4ab317ef18 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 724c7982565..3c825ba7b51 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 - 6.9.10-SNAPSHOT + 6.11.0-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 72ee9e9f8c0..60b5e8e6a2e 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index bc052942a09..9487e0d0ff0 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 07b6c1ae492..6c2a7425302 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 733d44e62ed..e494c7efe26 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index cb39d0a0092..45c1e7cfe03 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 05784706690..20f23bbf0a8 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 6b9fc930787..4d76e1727f6 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 - 6.9.10-SNAPSHOT + 6.11.0-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 dc1184e19b1..f6c0be183a7 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 - 6.9.10-SNAPSHOT + 6.11.0-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 72435ed7ffa..b2922d6fab3 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 - 6.9.10-SNAPSHOT + 6.11.0-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 5d9d9e68da6..e1e486f4809 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 - 6.9.10-SNAPSHOT + 6.11.0-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 7f57f2984ce..b5fd584064b 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 - 6.9.10-SNAPSHOT + 6.11.0-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 4ddb19ef8ef..e800a155f0c 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 02a0a156d27..2b22ccaa0e5 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index ce88ab99618..abed68a4f3d 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index e9487c22356..4e0ffaf8fcf 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index e17a02189a2..41eed268323 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.9.10-SNAPSHOT + 6.11.0-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 632c575bedb..b8861e78f3d 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 - 6.9.10-SNAPSHOT + 6.11.0-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 1348b36fe55..6045b9eae14 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 - 6.9.10-SNAPSHOT + 6.11.0-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 1f5d5ddf56a..ddcf6e02280 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 - 6.9.10-SNAPSHOT + 6.11.0-SNAPSHOT ../../pom.xml From 84e3becf1c9ecc6ca6586a312c79e519e4c5fc11 Mon Sep 17 00:00:00 2001 From: longma1 <32119004+longma1@users.noreply.github.com> Date: Wed, 25 Oct 2023 11:40:17 -0600 Subject: [PATCH 22/86] 6 8 5 mb (#5399) * version bump * Bump to core release 6.0.22 (#5028) * Bump to core release 6.0.16 * Bump to core version 6.0.20 * Fix errors thrown as a result of VersionSpecificWorkerContextWrapper * Bump to core 6.0.22 * Resolve 5126 hfj res ver prov might cause migration error on db that automatically indexes the primary key (#5127) * dropped old index FK_RESVERPROV_RES_PID on RES_PID column before adding IDX_RESVERPROV_RES_PID * added changelog * changed to valid version number * changed to valid version number, need to be ordered by version number... * 5123 - Use DEFAULT partition for server-based requests if none specified (#5124) 5123 - Use DEFAULT partition for server-based requests if none specified * consent remove all suppresses next link in bundle (#5119) * added FIXME with source of issue * added FIXME with root cause * added FIXME with root cause * Providing solution to the issue and removing fixmes. * Providing changelog * auto-formatting. * Adding new test. * Adding a new test for standard paging * let's try this and see if it works...? * fix tests * cleanup to trigger a new run * fixing tests --------- Co-authored-by: Ken Stevens Co-authored-by: peartree * 5117 MDM Score for No Match Fields Should Not Be Included in Total Score (#5118) * fix, test, changelog * fix, test, changelog --------- Co-authored-by: justindar * _source search parameter needs to support modifiers (#5095) _source search parameter needs to support modifiers - added support form :contains, :missing, :above modifiers * Fix HFQL docs (#5151) * Expunge operation on codesystem may throw 500 internal error with precondition fail message. (#5156) * Initial failing test. * Solution with changelog. * fixing format. * Addressing comment from code review. * fixing failing test. --------- Co-authored-by: peartree * documentation update (#5154) Co-authored-by: leif stawnyczy * Fix hsql jdbc driver deps (#5168) Avoid non-included classes in jdbc driver dependencies. * $delete-expunge over 10k resources will now delete all resources (#5144) * First commit with very rough fix and unit test. * Refinements to ResourceIdListStep and Batch2DaoSvcImpl. Make LoadIdsStepTest pass. Enhance Batch2DaoSvcImplTest. * Spotless * Fix checkstyle errors. * Fix test failures. * Minor refactoring. New unit test. Finalize changelist. * Spotless fix. * Delete now useless code from unit test. * Delete more useless code. * Test pre-commit hook * More spotless fixes. * Address most code review feedback. * Remove use of pageSize parameter and see if this breaks the pipeline. * Remove use of pageSize parameter and see if this breaks the pipeline. * Fix the noUrl case by passing an unlimited Pegeable instead. Effectively stop using page size for most databases. * Deprecate the old method and have it call the new one by default. * updating documentation (#5170) Co-authored-by: leif stawnyczy * _source search parameter modifiers for Subscription matching (#5159) * _source search parameter modifiers for Subscription matching - test, implementation and changelog * Removal of meta tags during updates do not trigger subscription (#5181) * Initial failing test. * adding solution; fixing documentation; * spotless apply * adding changelog * modifying current test --------- Co-authored-by: peartree * Issue 5173 get gateway everything doesnt return all patients (#5174) * Failing test * Also set offset and count in base DAO override * Changelog * Fix for specific case where count has been set in parameters * spotless * Improve checks --------- Co-authored-by: juan.marchionatto * Do not 500 and continue IG ingestion when different IGs try to save different ValueSets with colliding FHIR IDs (#5175) * First commit with failing unit test and small tweaks. * Swallow resource version exceptions from colliding ValueSet OIDs and log a descriptive error instead. Add more detailed unit testing. * Tweaks to logic and update the changelog. Reverse all changes to TermReadSvcImpl. * Revert PackageResourceParsingSvc to release branch baseline. * Accept code reviewer suggestion to change changelog description. Co-authored-by: michaelabuckley --------- Co-authored-by: michaelabuckley * Fix link * Remove target slf4j version * dont use new API for a bit (#5191) * Return DropIdGeneratorTask from the Builder to permit clients to mutate the (#5193) DropIdGeneratorTask. * Dqm performance bug update and provider loader fix (#5180) * update tests, move properties, update operation loader * update wip * remove test * fixing tests, adding config * update config and provider loader * fix bundles * fix cache settings on tests * version bump and change log * version bump * fix formatting * CVE-2022-45868 * wip cve change * cve h2 add back in --------- Co-authored-by: justin.mckelvy * bulkExportReuse with POST and GET (#5161) * string manipulation * Code to ensure bulkExportReuse works with POST and GET requests * Added formatting changes * Fixed tests that were not working * Formatting * Code clean up * fixing test failures * fixing test failures * Removed arrOfParams to now utilize ObjectMapper * Removing stack trace and adding an exception * Fixed test issue * formatting * formatting * Resolving code review comments * Reduce size of subscription max results (#5194) * Reduce MAX_SUBSCRIPTION_RESULTS to 10000 * Add changelog * 5037 goldenresource remains when target resource deleted (#5038) * draft test and fix * remove unused fields * remove unused fields * remove unused fields * draft test + solution for possible match case * combine sql statement + more error checking * add test case for possible duplicate * add config for autodeleting grs * refactoring, adding support for mongo, docs * refactoring + fixing mongo queries * add changelogs * fix both way link removal * clean up test comments * rename method * remove unnecessary bean * merge master/resolve conflicts * mvn spotless * address comment * changes to avoid version bumping * spotless * change error code --------- Co-authored-by: justindar * dont use new API for a bit (#5190) * licenses * Fixing terminology upload with large file. (#5204) * create initial test * adding fix; adding changelog; * fixing format. --------- Co-authored-by: peartree * up version to 6.8.0 from snapshot * fix up some duplicate deps for skipping * Force deployment of other projects * make dummy java class to pass sonatype checks * make dummy java class to pass sonatype checks * Cheeky test removal * Another test removal * Updating version to: 6.8.1 post release. * adding back in tests for release pipelines * Fix bad version insert * Bump to core library version 6.0.22.2 (#5261) * Bump to core library version 6.0.22.2 * Bump HAPI to 6.8.1-SNAPSHOT, core library version to 6.0.22.2 * up version to 6.8.1 * Prevent remote deployment * Updating version to: 6.8.2 post release. * Re-add deploy plugin skip * re-add version * Bump core library version to 6.0.22.2 * Bump HAPI to 6.8.2-SNAPSHOT * Fix up version enum * Version bump * Updating version to: 6.8.3 post release. * Make display name validation configurable (#5321) * Test fixes * Build cleanup * Initial test passing * Test fixes * Tests all seem to be working * Make display validation level configurable * Should be all working * Add changelog * Add to changelog --------- Co-authored-by: Tadgh * wip * wip * Fix conflicts * add backport info * Fix versioning * test fixes * disabled 1 test * backported 5271 to fix validation message tests * wip * Changelog * Fix tests * Fix backport * Add upgrade.md * Fix test * Fix key * Updating version to: 6.8.4 post release. * 5353 iterate on revincludes and includes does not return correct resources when used with non iterate revincludes (#5354) * added failing test * implemented solution * added doc generation * added changelog * fixed the order of includes and revincludes, based on what was implemented before * fixed formatting * fixed wording --------- Co-authored-by: Steven Li * added changelog * Updating version to: 6.8.5 post release. * merge back 5377 * changelog * undo unintended changes from mergeback --------- Co-authored-by: tadgh Co-authored-by: dotasek Co-authored-by: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: Nathan Doef Co-authored-by: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Co-authored-by: TipzCM Co-authored-by: leif stawnyczy Co-authored-by: michaelabuckley Co-authored-by: Luke deGruchy Co-authored-by: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Co-authored-by: juan.marchionatto Co-authored-by: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Co-authored-by: justin.mckelvy Co-authored-by: LalithE <132382565+LalithE@users.noreply.github.com> Co-authored-by: Long Ma Co-authored-by: markiantorno Co-authored-by: volodymyr Co-authored-by: James Agnew Co-authored-by: StevenXLi Co-authored-by: Steven Li --- .../src/main/java/ca/uhn/fhir/util/VersionEnum.java | 3 +++ .../ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md | 1 + .../ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml | 3 +++ .../ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md | 0 .../ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml | 3 +++ .../ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md | 0 .../ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml | 3 +++ .../5271-improve-code-validation-error-messages.yaml | 9 +++++++++ .../5321-code-display-validation-now-configurable.yaml | 9 +++++++++ .../changelog/7_0_0/5333-prefix-regression-export.yaml | 6 ++++++ ...resources-when-used-with-non-iterate-revincludes.yaml | 7 +++++++ .../5377-improve-subscription-triggering-speed.yaml | 6 ++++++ .../fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java | 1 + 13 files changed, 51 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index 70f2a2bd4c7..bb2d3a0146c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -123,6 +123,9 @@ public enum VersionEnum { V6_8_0, V6_8_1, V6_8_2, + V6_8_3, + V6_8_4, + V6_8_5, V6_9_0, V6_10_0, diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md new file mode 100644 index 00000000000..42b7c037d9b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md @@ -0,0 +1 @@ +This release permits you to change the severity of a coding display mismatch error during validation. This also fixes a regression which was causing Bulk Export `_exportId` to not be respected during blob prefixing. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml new file mode 100644 index 00000000000..a8d336beddb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-09-22" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml new file mode 100644 index 00000000000..ebcd1754c79 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-10-18" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml new file mode 100644 index 00000000000..50b571c2a96 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-10-20" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml new file mode 100644 index 00000000000..13bb268ebca --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml @@ -0,0 +1,9 @@ +--- +type: add +issue: 5271 +backport: 6.8.3 +title: "The error messages returned in an OperationOutcome when validating terminology codes + as a part of resource profile validation have been improved. Machine processable location + (line/col) information is now available through a pair of dedicated extensions, and + error messages such as UCUM parsing issues are now returned to the client (previously + they were swallowed and a generic error message was returned)." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml new file mode 100644 index 00000000000..e5268283d39 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml @@ -0,0 +1,9 @@ +--- +type: add +issue: 5321 +backport: 6.8.3 +title: "It is now possible to configure the strictness of concept display name validation + using a new flag on the InMemoryTerminologyServerValidationSupport (for non-JPA validation) + and JpaStorageSettings (for JPA validation). In addition, the error messages emitted by + the validator when a concept display doesn't match have been improved to be much + more useful." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml new file mode 100644 index 00000000000..c7907622074 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5333 +backport: 6.8.3 +jira: SMILE-7403 +title: "A regression was introduced in 2023.08.R01 which caused binary storage prefixes to not be applied to exported binary blobs. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml new file mode 100644 index 00000000000..d4f8667914d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5353 +jira: SMILE-7451 +backport: 6.8.4 +title: "Previously, when using revincludes and includes with iterate, while also using revincludes without iterate, +the result omitted some resources that should have been included. This issue has now been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml new file mode 100644 index 00000000000..6577dc1f14a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 5377 +jira: SMILE-7545 +backport: 6.8.5 +title: "Subscription triggering via the `$trigger-subscription` operation is now multi-threaded, which significantly improves performance for large data sets." diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java index 7faf5984eea..0c1b142501c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java @@ -27,6 +27,7 @@ import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; 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.transaction.annotation.Transactional; From a7a446903fd949fc08b584b7efbf4d986b8a5e86 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Wed, 25 Oct 2023 23:35:20 -0400 Subject: [PATCH 23/86] 5401 add terserutil clear by fhirpath (#5402) * begin with failing test * test passes * test passes * changelog * moar testor --- .../context/BaseRuntimeChildDefinition.java | 5 + .../java/ca/uhn/fhir/parser/JsonParser.java | 21 +- .../java/ca/uhn/fhir/util/TerserUtil.java | 34 ++- .../7_0_0/5401-terserutil-clear.yaml | 5 + .../java/ca/uhn/fhir/util/TerserUtilTest.java | 267 +++++++++++------- 5 files changed, 210 insertions(+), 122 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java index bafc0cce4ce..e55d2fa2b25 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.annotation.Child; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -78,6 +79,10 @@ public abstract class BaseRuntimeChildDefinition { this.myReplacedParentDefinition = myReplacedParentDefinition; } + public boolean isMultipleCardinality() { + return this.getMax() > 1 || this.getMax() == Child.MAX_UNLIMITED; + } + public interface IAccessor { List getValues(IBase theTarget); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 846d5dab271..f82222b4ddb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -39,7 +39,6 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.primitive.IdDt; @@ -659,9 +658,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { theEventWriter.endArray(); } BaseRuntimeChildDefinition replacedParentDefinition = nextChild.getReplacedParentDefinition(); - if (isMultipleCardinality(nextChild.getMax()) - || (replacedParentDefinition != null - && isMultipleCardinality(replacedParentDefinition.getMax()))) { + if (nextChild.isMultipleCardinality() + || (replacedParentDefinition != null && replacedParentDefinition.isMultipleCardinality())) { beginArray(theEventWriter, nextChildSpecificName); inArray = true; encodeChildElementToStreamWriter( @@ -728,14 +726,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { List heldModExts = Collections.emptyList(); if (extensions.size() > i && extensions.get(i) != null - && extensions.get(i).isEmpty() == false) { + && !extensions.get(i).isEmpty()) { haveContent = true; heldExts = extensions.get(i); } if (modifierExtensions.size() > i && modifierExtensions.get(i) != null - && modifierExtensions.get(i).isEmpty() == false) { + && !modifierExtensions.get(i).isEmpty()) { haveContent = true; heldModExts = modifierExtensions.get(i); } @@ -746,7 +744,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } else { nextComments = null; } - if (nextComments != null && nextComments.isEmpty() == false) { + if (nextComments != null && !nextComments.isEmpty()) { haveContent = true; } @@ -804,10 +802,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { return myIsSupportsFhirComment; } - private boolean isMultipleCardinality(int maxCardinality) { - return maxCardinality > 1 || maxCardinality == Child.MAX_UNLIMITED; - } - private void encodeCompositeElementToStreamWriter( RuntimeResourceDefinition theResDef, IBaseResource theResource, @@ -917,10 +911,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { // Undeclared extensions extractUndeclaredExtensions( theResourceId, extensions, modifierExtensions, null, null, theEncodeContext, theContainedResource); - boolean haveExtension = false; - if (!extensions.isEmpty()) { - haveExtension = true; - } + boolean haveExtension = !extensions.isEmpty(); if (theResourceId.hasFormatComment() || haveExtension) { beginObject(theEventWriter, "_id"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java index 11b416fdbbf..d20fe991c16 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java @@ -351,6 +351,29 @@ public final class TerserUtil { clear(childDefinition.getAccessor().getValues(theResource)); } + /** + * Clears the specified field on the resource provided by the FHIRPath. If more than one value matches + * the FHIRPath, all values will be cleared. + * + * @param theFhirContext + * @param theResource + * @param theFhirPath + */ + public static void clearFieldByFhirPath(FhirContext theFhirContext, IBaseResource theResource, String theFhirPath) { + + if (theFhirPath.contains(".")) { + String parentPath = theFhirPath.substring(0, theFhirPath.lastIndexOf(".")); + String fieldName = theFhirPath.substring(theFhirPath.lastIndexOf(".") + 1); + FhirTerser terser = theFhirContext.newTerser(); + List parents = terser.getValues(theResource, parentPath); + for (IBase parent : parents) { + clearField(theFhirContext, fieldName, parent); + } + } else { + clearField(theFhirContext, theResource, theFhirPath); + } + } + /** * Clears the specified field on the element provided * @@ -362,7 +385,16 @@ public final class TerserUtil { BaseRuntimeElementDefinition definition = theFhirContext.getElementDefinition(theBase.getClass()); BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); Validate.notNull(childDefinition); - clear(childDefinition.getAccessor().getValues(theBase)); + BaseRuntimeChildDefinition.IAccessor accessor = childDefinition.getAccessor(); + clear(accessor.getValues(theBase)); + List newValue = accessor.getValues(theBase); + + if (newValue != null && !newValue.isEmpty()) { + // Our clear failed, probably because it was an immutable SingletonList returned by a FieldPlainAccessor + // that cannot be cleared. + // Let's just null it out instead. + childDefinition.getMutator().setValue(theBase, null); + } } /** diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml new file mode 100644 index 00000000000..733f62c5bc7 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5401 +title: "Previously, it was only possible to clear a top-level field on a resource using TerserUtil. A new method has been added +to TerserUtil to support clearing a value by FhirPath." diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java index 493f2eb05b9..ddf94c26105 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java @@ -13,6 +13,7 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; import java.util.Date; @@ -30,88 +31,88 @@ class TerserUtilTest { private FhirContext ourFhirContext = FhirContext.forR4(); private static final String SAMPLE_PERSON = - """ - { - "resourceType": "Patient", - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "valueCoding": { - "system": "MyInternalRace", - "code": "X", - "display": "Eks" + """ + { + "resourceType": "Patient", + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "valueCoding": { + "system": "MyInternalRace", + "code": "X", + "display": "Eks" + } + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity'", + "valueCoding": { + "system": "MyInternalEthnicity", + "display": "NNN" + } } - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity'", - "valueCoding": { - "system": "MyInternalEthnicity", - "display": "NNN" + ], + "identifier": [ + { + "system": "http://example.org/member_id", + "value": "123123" + }, + { + "system": "http://example.org/medicaid_id", + "value": "12312323123Z" + }, + { + "system": "http://example.org/CDNS_id", + "value": "123123123E" + }, + { + "system": "http://example.org/SSN" } - } - ], - "identifier": [ - { - "system": "http://example.org/member_id", - "value": "123123" - }, - { - "system": "http://example.org/medicaid_id", - "value": "12312323123Z" - }, - { - "system": "http://example.org/CDNS_id", - "value": "123123123E" - }, - { - "system": "http://example.org/SSN" - } - ], - "active": true, - "name": [ - { - "family": "TestFamily", - "given": [ - "Given" - ] - } - ], - "telecom": [ - { - "system": "email", - "value": "email@email.io" - }, - { - "system": "phone", - "value": "123123231" - }, - { - "system": "phone", - "value": "1231232312" - }, - { - "system": "phone", - "value": "1231232314" - } - ], - "gender": "male", - "birthDate": "1900-01-01", - "deceasedBoolean": true, - "contained": [ - { - "id": "1", - "identifier": [ - { - "system": "urn:hssc:srhs:contact:organizationId", - "value": "1000" - } - ], - "name": "BUILDERS FIRST SOURCE", - "resourceType": "Organization" - } - ] - } - """; + ], + "active": true, + "name": [ + { + "family": "TestFamily", + "given": [ + "Given" + ] + } + ], + "telecom": [ + { + "system": "email", + "value": "email@email.io" + }, + { + "system": "phone", + "value": "123123231" + }, + { + "system": "phone", + "value": "1231232312" + }, + { + "system": "phone", + "value": "1231232314" + } + ], + "gender": "male", + "birthDate": "1900-01-01", + "deceasedBoolean": true, + "contained": [ + { + "id": "1", + "identifier": [ + { + "system": "urn:hssc:srhs:contact:organizationId", + "value": "1000" + } + ], + "name": "BUILDERS FIRST SOURCE", + "resourceType": "Organization" + } + ] + } + """; @Test void testCloneEidIntoResource() { @@ -180,7 +181,7 @@ class TerserUtilTest { RuntimeResourceDefinition definition = p1Helper.getResourceDefinition(); TerserUtil.cloneEidIntoResource(ourFhirContext, definition.getChildByName("identifier"), - p1.getIdentifier().get(0), p2Helper.getResource()); + p1.getIdentifier().get(0), p2Helper.getResource()); assertEquals(1, p2Helper.getFieldValues("identifier").size()); @@ -291,12 +292,12 @@ class TerserUtilTest { Patient p1 = new Patient(); p1.addAddress() - .addLine("10 Main Street") - .setCity("Hamilton") - .setState("ON") - .setPostalCode("Z0Z0Z0") - .setCountry("Canada") - .addExtension(ext); + .addLine("10 Main Street") + .setCity("Hamilton") + .setState("ON") + .setPostalCode("Z0Z0Z0") + .setCountry("Canada") + .addExtension(ext); Patient p2 = new Patient(); p2.addAddress().addLine("10 Lenin Street").setCity("Severodvinsk").setCountry("Russia"); @@ -328,12 +329,12 @@ class TerserUtilTest { Patient p1 = new Patient(); p1.addAddress() - .addLine("10 Main Street") - .setCity("Hamilton") - .setState("ON") - .setPostalCode("Z0Z0Z0") - .setCountry("Canada") - .addExtension(ext); + .addLine("10 Main Street") + .setCity("Hamilton") + .setState("ON") + .setPostalCode("Z0Z0Z0") + .setCountry("Canada") + .addExtension(ext); Patient p2 = new Patient(); p2.addAddress().addLine("10 Lenin Street").setCity("Severodvinsk").setCountry("Russia"); @@ -353,21 +354,21 @@ class TerserUtilTest { Patient p1 = new Patient(); p1.addAddress() - .addLine("10 Main Street") - .setCity("Hamilton") - .setState("ON") - .setPostalCode("Z0Z0Z0") - .setCountry("Canada") - .addExtension(ext); + .addLine("10 Main Street") + .setCity("Hamilton") + .setState("ON") + .setPostalCode("Z0Z0Z0") + .setCountry("Canada") + .addExtension(ext); Patient p2 = new Patient(); p2.addAddress() - .addLine("10 Main Street") - .setCity("Hamilton") - .setState("ON") - .setPostalCode("Z0Z0Z1") - .setCountry("Canada") - .addExtension(ext); + .addLine("10 Main Street") + .setCity("Hamilton") + .setState("ON") + .setPostalCode("Z0Z0Z1") + .setCountry("Canada") + .addExtension(ext); TerserUtil.mergeField(ourFhirContext, "address", p1, p2); @@ -469,7 +470,8 @@ class TerserUtilTest { assertEquals(1, p2.getName().size()); assertEquals("Doe", p2.getName().get(0).getFamily()); } - @Test + + @Test public void testReplaceFieldByEmptyValue() { Patient p1 = new Patient(); Patient p2 = new Patient(); @@ -507,6 +509,7 @@ class TerserUtilTest { { Patient p1 = new Patient(); p1.addName().setFamily("Doe"); + assertEquals(1, p1.getName().size()); TerserUtil.clearField(ourFhirContext, p1, "name"); @@ -517,6 +520,7 @@ class TerserUtilTest { Address a1 = new Address(); a1.addLine("Line 1"); a1.addLine("Line 2"); + assertEquals(2, a1.getLine().size()); a1.setCity("Test"); TerserUtil.clearField(ourFhirContext, "line", a1); @@ -525,6 +529,47 @@ class TerserUtilTest { } } + @Test + public void testClearFieldByFhirPath() { + Patient p1 = new Patient(); + p1.addName().setFamily("Doe"); + assertEquals(1, p1.getName().size()); + + TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "name"); + + assertEquals(0, p1.getName().size()); + + Address a1 = new Address(); + a1.addLine("Line 1"); + a1.addLine("Line 2"); + assertEquals(2, a1.getLine().size()); + a1.setCity("Test"); + a1.getPeriod().setStartElement(new DateTimeType("2021-01-01")); + p1.addAddress(a1); + + assertEquals("2021-01-01", p1.getAddress().get(0).getPeriod().getStartElement().toHumanDisplay()); + assertNotNull(p1.getAddress().get(0).getPeriod().getStart()); + + Address a2 = new Address(); + a2.addLine("Line 1"); + a2.addLine("Line 2"); + a2.setCity("Test"); + a2.getPeriod().setStartElement(new DateTimeType("2021-01-01")); + p1.addAddress(a2); + + + TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "address.line"); + TerserUtil.clearFieldByFhirPath(ourFhirContext, p1, "address.period.start"); + + assertNull(p1.getAddress().get(0).getPeriod().getStart()); + + assertEquals(2, p1.getAddress().size()); + assertEquals(0, p1.getAddress().get(0).getLine().size()); + assertEquals(0, p1.getAddress().get(1).getLine().size()); + assertEquals("Test", p1.getAddress().get(0).getCity()); + assertEquals("Test", p1.getAddress().get(1).getCity()); + } + @Test public void testSetField() { Patient p1 = new Patient(); @@ -551,6 +596,16 @@ class TerserUtilTest { assertEquals("CITY", p1.getAddress().get(0).getCity()); } + @Test + public void testSetFieldByCompositeFhirPath() { + Patient p1 = new Patient(); + + TerserUtil.setFieldByFhirPath(ourFhirContext, "address.city", p1, new StringType("CITY")); + + assertEquals(1, p1.getAddress().size()); + assertEquals("CITY", p1.getAddress().get(0).getCity()); + } + @Test public void testClone() { Patient p1 = new Patient(); From c89fc46863078c7e66cd333054b7f9b3f711195f Mon Sep 17 00:00:00 2001 From: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Date: Tue, 31 Oct 2023 13:21:35 -0400 Subject: [PATCH 24/86] =?UTF-8?q?Add=20ITermConceptClientMappingSvc=20inte?= =?UTF-8?q?rface=20to=20represent=20terminology=20t=E2=80=A6=20(#5410)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add ITermConceptClientMappingSvc interface to represent terminology translate functions * Add javadoc --------- Co-authored-by: juan.marchionatto --- .../term/TermConceptClientMappingSvcImpl.java | 420 ++++++++++++++++++ .../jpa/term/TermConceptMappingSvcImpl.java | 398 +---------------- .../api/ITermConceptClientMappingSvc.java | 34 ++ .../jpa/term/api/ITermConceptMappingSvc.java | 12 +- 4 files changed, 460 insertions(+), 404 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java 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 new file mode 100644 index 00000000000..afee40feeab --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java @@ -0,0 +1,420 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.TranslateConceptResult; +import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.model.TranslationQuery; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.api.svc.IIdHelperService; +import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; +import ca.uhn.fhir.jpa.entity.TermConceptMap; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.term.api.ITermConceptClientMappingSvc; +import ca.uhn.fhir.jpa.util.MemoryCacheService; +import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.apache.commons.lang3.StringUtils; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.persistence.PersistenceContextType; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class TermConceptClientMappingSvcImpl implements ITermConceptClientMappingSvc { + private static final Logger ourLog = LoggerFactory.getLogger(TermConceptClientMappingSvcImpl.class); + + private final int myFetchSize = TermReadSvcImpl.DEFAULT_FETCH_SIZE; + + protected static boolean ourLastResultsFromTranslationCache; // For testing. + protected static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. + + @PersistenceContext(type = PersistenceContextType.TRANSACTION) + protected EntityManager myEntityManager; + + @Autowired + protected FhirContext myContext; + + @Autowired + protected MemoryCacheService myMemoryCacheService; + + @Autowired + protected IIdHelperService myIdHelperService; + + @Autowired + protected ITermConceptMapDao myConceptMapDao; + + @Override + @Transactional(propagation = Propagation.REQUIRED) + public TranslateConceptResults translate(TranslationRequest theTranslationRequest) { + TranslateConceptResults retVal = new TranslateConceptResults(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = + criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class); + Root root = query.from(TermConceptMapGroupElementTarget.class); + + Join elementJoin = + root.join("myConceptMapGroupElement"); + Join groupJoin = elementJoin.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedTargets; + ArrayList predicates; + Coding coding; + + // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + + for (TranslationQuery translationQuery : translationQueries) { + cachedTargets = myMemoryCacheService.getIfPresent( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery); + if (cachedTargets == null) { + final List targets = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode())); + } else { + throw new InvalidRequestException( + Msg.code(842) + "A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem())); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion())); + } + + if (translationQuery.hasTargetSystem()) { + predicates.add( + criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem())); + } + + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal( + conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add( + criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget())); + } + + if (translationQuery.hasResourceId()) { + IIdType resourceId = translationQuery.getResourceId(); + JpaPid resourcePid = + myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = + myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = + (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + try (ScrollableResultsIterator scrollableResultsIterator = + new ScrollableResultsIterator<>(scrollableResults)) { + + Set matches = new HashSet<>(); + while (scrollableResultsIterator.hasNext()) { + TermConceptMapGroupElementTarget next = scrollableResultsIterator.next(); + if (matches.add(next)) { + + TranslateConceptResult translationMatch = new TranslateConceptResult(); + if (next.getEquivalence() != null) { + translationMatch.setEquivalence( + next.getEquivalence().toCode()); + } + + translationMatch.setCode(next.getCode()); + translationMatch.setSystem(next.getSystem()); + translationMatch.setSystemVersion(next.getSystemVersion()); + translationMatch.setDisplay(next.getDisplay()); + translationMatch.setValueSet(next.getValueSet()); + translationMatch.setSystemVersion(next.getSystemVersion()); + translationMatch.setConceptMapUrl(next.getConceptMapUrl()); + + targets.add(translationMatch); + } + } + } + + ourLastResultsFromTranslationCache = false; // For testing. + myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery, targets); + retVal.getResults().addAll(targets); + } else { + ourLastResultsFromTranslationCache = true; // For testing. + retVal.getResults().addAll(cachedTargets); + } + } + + buildTranslationResult(retVal); + return retVal; + } + + @Override + @Transactional(propagation = Propagation.REQUIRED) + public TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest) { + TranslateConceptResults retVal = new TranslateConceptResults(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class); + Root root = query.from(TermConceptMapGroupElement.class); + + Join targetJoin = + root.join("myConceptMapGroupElementTargets"); + Join groupJoin = root.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedElements; + ArrayList predicates; + Coding coding; + + // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + + for (TranslationQuery translationQuery : translationQueries) { + cachedElements = myMemoryCacheService.getIfPresent( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery); + if (cachedElements == null) { + final List elements = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + String targetCode; + String targetCodeSystem = null; + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); + targetCode = coding.getCode(); + } else { + throw new InvalidRequestException( + Msg.code(843) + "A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem())); + targetCodeSystem = coding.getSystem(); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); + } + + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal( + conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add( + criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + + if (translationQuery.hasTargetSystem()) { + predicates.add( + criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem())); + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget())); + } + + if (translationQuery.hasResourceId()) { + IIdType resourceId = translationQuery.getResourceId(); + JpaPid resourcePid = + myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = + myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = + (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + try (ScrollableResultsIterator scrollableResultsIterator = + new ScrollableResultsIterator<>(scrollableResults)) { + + Set matches = new HashSet<>(); + while (scrollableResultsIterator.hasNext()) { + TermConceptMapGroupElement nextElement = scrollableResultsIterator.next(); + + /* TODO: The invocation of the size() below does not seem to be necessary but for some reason, + * but removing it causes tests in TerminologySvcImplR4Test to fail. We use the outcome + * in a trace log to avoid ErrorProne flagging an unused return value. + */ + int size = + nextElement.getConceptMapGroupElementTargets().size(); + ourLog.trace("Have {} targets", size); + + myEntityManager.detach(nextElement); + + if (isNotBlank(targetCode)) { + for (TermConceptMapGroupElementTarget next : + nextElement.getConceptMapGroupElementTargets()) { + if (matches.add(next)) { + if (isBlank(targetCodeSystem) + || StringUtils.equals(targetCodeSystem, next.getSystem())) { + if (StringUtils.equals(targetCode, next.getCode())) { + TranslateConceptResult translationMatch = new TranslateConceptResult(); + translationMatch.setCode(nextElement.getCode()); + translationMatch.setSystem(nextElement.getSystem()); + translationMatch.setSystemVersion(nextElement.getSystemVersion()); + translationMatch.setDisplay(nextElement.getDisplay()); + translationMatch.setValueSet(nextElement.getValueSet()); + translationMatch.setSystemVersion(nextElement.getSystemVersion()); + translationMatch.setConceptMapUrl(nextElement.getConceptMapUrl()); + if (next.getEquivalence() != null) { + translationMatch.setEquivalence( + next.getEquivalence().toCode()); + } + + if (alreadyContainsMapping(elements, translationMatch) + || alreadyContainsMapping(retVal.getResults(), translationMatch)) { + continue; + } + + elements.add(translationMatch); + } + } + } + } + } + } + } + + ourLastResultsFromTranslationWithReverseCache = false; // For testing. + myMemoryCacheService.put( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery, elements); + retVal.getResults().addAll(elements); + } else { + ourLastResultsFromTranslationWithReverseCache = true; // For testing. + retVal.getResults().addAll(cachedElements); + } + } + + buildTranslationResult(retVal); + return retVal; + } + + @Override + public FhirContext getFhirContext() { + return myContext; + } + + // Special case for the translate operation with url and without + // conceptMapVersion, find the latest conecptMapVersion + private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { + + Pageable page = PageRequest.of(0, 1); + List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate( + page, theTranslationRequest.getUrl()); + if (!theConceptMapList.isEmpty()) { + return theConceptMapList.get(0).getVersion(); + } + + return null; + } + + private void buildTranslationResult(TranslateConceptResults theTranslationResult) { + + String msg; + if (theTranslationResult.getResults().isEmpty()) { + theTranslationResult.setResult(false); + msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound"); + theTranslationResult.setMessage(msg); + } else { + theTranslationResult.setResult(true); + msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound"); + theTranslationResult.setMessage(msg); + } + } + + private boolean alreadyContainsMapping( + List elements, TranslateConceptResult translationMatch) { + for (TranslateConceptResult nextExistingElement : elements) { + if (StringUtils.equals(nextExistingElement.getSystem(), translationMatch.getSystem())) { + if (StringUtils.equals(nextExistingElement.getSystemVersion(), translationMatch.getSystemVersion())) { + if (StringUtils.equals(nextExistingElement.getCode(), translationMatch.getCode())) { + return true; + } + } + } + } + return false; + } +} 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 d4c7928b62a..781f5e281ac 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 @@ -19,15 +19,10 @@ */ package ca.uhn.fhir.jpa.term; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.TranslateConceptResult; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.model.TranslationQuery; import ca.uhn.fhir.jpa.api.model.TranslationRequest; -import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; @@ -35,21 +30,13 @@ import ca.uhn.fhir.jpa.entity.TermConceptMap; import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; -import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; -import ca.uhn.fhir.jpa.util.MemoryCacheService; -import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.StringUtils; -import org.hibernate.ScrollMode; -import org.hibernate.ScrollableResults; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; @@ -61,39 +48,17 @@ import org.hl7.fhir.r4.model.UriType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.Optional; -import java.util.Set; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.term.TermReadSvcImpl.isPlaceholder; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { +public class TermConceptMappingSvcImpl extends TermConceptClientMappingSvcImpl implements ITermConceptMappingSvc { private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImpl.class); - private static boolean ourLastResultsFromTranslationCache; // For testing. - private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. - private final int myFetchSize = TermReadSvcImpl.DEFAULT_FETCH_SIZE; - - @Autowired - protected ITermConceptMapDao myConceptMapDao; @Autowired protected ITermConceptMapGroupDao myConceptMapGroupDao; @@ -104,29 +69,12 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { @Autowired protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao; - @PersistenceContext(type = PersistenceContextType.TRANSACTION) - protected EntityManager myEntityManager; - - @Autowired - private FhirContext myContext; - - @Autowired - private MemoryCacheService myMemoryCacheService; - - @Autowired - private IIdHelperService myIdHelperService; - @Override @Transactional public void deleteConceptMapAndChildren(ResourceTable theResourceTable) { deleteConceptMap(theResourceTable); } - @Override - public FhirContext getFhirContext() { - return myContext; - } - @Override @Transactional public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { @@ -205,7 +153,7 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); } - if (!optionalExistingTermConceptMapByUrl.isPresent()) { + if (optionalExistingTermConceptMapByUrl.isEmpty()) { try { if (isNotBlank(source)) { termConceptMap.setSource(source); @@ -328,320 +276,6 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { theConceptMap.getIdElement().toVersionless().getValueAsString()); } - @Override - @Transactional(propagation = Propagation.REQUIRED) - public TranslateConceptResults translate(TranslationRequest theTranslationRequest) { - TranslateConceptResults retVal = new TranslateConceptResults(); - - CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery query = - criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class); - Root root = query.from(TermConceptMapGroupElementTarget.class); - - Join elementJoin = - root.join("myConceptMapGroupElement"); - Join groupJoin = elementJoin.join("myConceptMapGroup"); - Join conceptMapJoin = groupJoin.join("myConceptMap"); - - List translationQueries = theTranslationRequest.getTranslationQueries(); - List cachedTargets; - ArrayList predicates; - Coding coding; - - // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version - String latestConceptMapVersion = null; - if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) - latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); - - for (TranslationQuery translationQuery : translationQueries) { - cachedTargets = myMemoryCacheService.getIfPresent( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery); - if (cachedTargets == null) { - final List targets = new ArrayList<>(); - - predicates = new ArrayList<>(); - - coding = translationQuery.getCoding(); - if (coding.hasCode()) { - predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode())); - } else { - throw new InvalidRequestException( - Msg.code(842) + "A code must be provided for translation to occur."); - } - - if (coding.hasSystem()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem())); - } - - if (coding.hasVersion()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion())); - } - - if (translationQuery.hasTargetSystem()) { - predicates.add( - criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem())); - } - - if (translationQuery.hasUrl()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); - if (translationQuery.hasConceptMapVersion()) { - // both url and conceptMapVersion - predicates.add(criteriaBuilder.equal( - conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); - } else { - if (StringUtils.isNotBlank(latestConceptMapVersion)) { - // only url and use latestConceptMapVersion - predicates.add( - criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); - } else { - predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); - } - } - } - - if (translationQuery.hasSource()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource())); - } - - if (translationQuery.hasTarget()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget())); - } - - if (translationQuery.hasResourceId()) { - IIdType resourceId = translationQuery.getResourceId(); - JpaPid resourcePid = - myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); - } - - Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); - query.where(outerPredicate); - - // Use scrollable results. - final TypedQuery typedQuery = - myEntityManager.createQuery(query.select(root)); - org.hibernate.query.Query hibernateQuery = - (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - try (ScrollableResultsIterator scrollableResultsIterator = - new ScrollableResultsIterator<>(scrollableResults)) { - - Set matches = new HashSet<>(); - while (scrollableResultsIterator.hasNext()) { - TermConceptMapGroupElementTarget next = scrollableResultsIterator.next(); - if (matches.add(next)) { - - TranslateConceptResult translationMatch = new TranslateConceptResult(); - if (next.getEquivalence() != null) { - translationMatch.setEquivalence( - next.getEquivalence().toCode()); - } - - translationMatch.setCode(next.getCode()); - translationMatch.setSystem(next.getSystem()); - translationMatch.setSystemVersion(next.getSystemVersion()); - translationMatch.setDisplay(next.getDisplay()); - translationMatch.setValueSet(next.getValueSet()); - translationMatch.setSystemVersion(next.getSystemVersion()); - translationMatch.setConceptMapUrl(next.getConceptMapUrl()); - - targets.add(translationMatch); - } - } - } - - ourLastResultsFromTranslationCache = false; // For testing. - myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery, targets); - retVal.getResults().addAll(targets); - } else { - ourLastResultsFromTranslationCache = true; // For testing. - retVal.getResults().addAll(cachedTargets); - } - } - - buildTranslationResult(retVal); - return retVal; - } - - @Override - @Transactional(propagation = Propagation.REQUIRED) - public TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest) { - TranslateConceptResults retVal = new TranslateConceptResults(); - - CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class); - Root root = query.from(TermConceptMapGroupElement.class); - - Join targetJoin = - root.join("myConceptMapGroupElementTargets"); - Join groupJoin = root.join("myConceptMapGroup"); - Join conceptMapJoin = groupJoin.join("myConceptMap"); - - List translationQueries = theTranslationRequest.getTranslationQueries(); - List cachedElements; - ArrayList predicates; - Coding coding; - - // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version - String latestConceptMapVersion = null; - if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) - latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); - - for (TranslationQuery translationQuery : translationQueries) { - cachedElements = myMemoryCacheService.getIfPresent( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery); - if (cachedElements == null) { - final List elements = new ArrayList<>(); - - predicates = new ArrayList<>(); - - coding = translationQuery.getCoding(); - String targetCode; - String targetCodeSystem = null; - if (coding.hasCode()) { - predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); - targetCode = coding.getCode(); - } else { - throw new InvalidRequestException( - Msg.code(843) + "A code must be provided for translation to occur."); - } - - if (coding.hasSystem()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem())); - targetCodeSystem = coding.getSystem(); - } - - if (coding.hasVersion()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); - } - - if (translationQuery.hasUrl()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); - if (translationQuery.hasConceptMapVersion()) { - // both url and conceptMapVersion - predicates.add(criteriaBuilder.equal( - conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); - } else { - if (StringUtils.isNotBlank(latestConceptMapVersion)) { - // only url and use latestConceptMapVersion - predicates.add( - criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); - } else { - predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); - } - } - } - - if (translationQuery.hasTargetSystem()) { - predicates.add( - criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem())); - } - - if (translationQuery.hasSource()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource())); - } - - if (translationQuery.hasTarget()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget())); - } - - if (translationQuery.hasResourceId()) { - IIdType resourceId = translationQuery.getResourceId(); - JpaPid resourcePid = - myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); - } - - Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); - query.where(outerPredicate); - - // Use scrollable results. - final TypedQuery typedQuery = - myEntityManager.createQuery(query.select(root)); - org.hibernate.query.Query hibernateQuery = - (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - try (ScrollableResultsIterator scrollableResultsIterator = - new ScrollableResultsIterator<>(scrollableResults)) { - - Set matches = new HashSet<>(); - while (scrollableResultsIterator.hasNext()) { - TermConceptMapGroupElement nextElement = scrollableResultsIterator.next(); - - /* TODO: The invocation of the size() below does not seem to be necessary but for some reason, - * but removing it causes tests in TerminologySvcImplR4Test to fail. We use the outcome - * in a trace log to avoid ErrorProne flagging an unused return value. - */ - int size = - nextElement.getConceptMapGroupElementTargets().size(); - ourLog.trace("Have {} targets", size); - - myEntityManager.detach(nextElement); - - if (isNotBlank(targetCode)) { - for (TermConceptMapGroupElementTarget next : - nextElement.getConceptMapGroupElementTargets()) { - if (matches.add(next)) { - if (isBlank(targetCodeSystem) - || StringUtils.equals(targetCodeSystem, next.getSystem())) { - if (StringUtils.equals(targetCode, next.getCode())) { - TranslateConceptResult translationMatch = new TranslateConceptResult(); - translationMatch.setCode(nextElement.getCode()); - translationMatch.setSystem(nextElement.getSystem()); - translationMatch.setSystemVersion(nextElement.getSystemVersion()); - translationMatch.setDisplay(nextElement.getDisplay()); - translationMatch.setValueSet(nextElement.getValueSet()); - translationMatch.setSystemVersion(nextElement.getSystemVersion()); - translationMatch.setConceptMapUrl(nextElement.getConceptMapUrl()); - if (next.getEquivalence() != null) { - translationMatch.setEquivalence( - next.getEquivalence().toCode()); - } - - if (alreadyContainsMapping(elements, translationMatch) - || alreadyContainsMapping(retVal.getResults(), translationMatch)) { - continue; - } - - elements.add(translationMatch); - } - } - } - } - } - } - } - - ourLastResultsFromTranslationWithReverseCache = false; // For testing. - myMemoryCacheService.put( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery, elements); - retVal.getResults().addAll(elements); - } else { - ourLastResultsFromTranslationWithReverseCache = true; // For testing. - retVal.getResults().addAll(cachedElements); - } - } - - buildTranslationResult(retVal); - return retVal; - } - - private boolean alreadyContainsMapping( - List elements, TranslateConceptResult translationMatch) { - for (TranslateConceptResult nextExistingElement : elements) { - if (StringUtils.equals(nextExistingElement.getSystem(), translationMatch.getSystem())) { - if (StringUtils.equals(nextExistingElement.getSystemVersion(), translationMatch.getSystemVersion())) { - if (StringUtils.equals(nextExistingElement.getCode(), translationMatch.getCode())) { - return true; - } - } - } - } - return false; - } - public void deleteConceptMap(ResourceTable theResourceTable) { // Get existing entity so it can be deleted. Optional optionalExistingTermConceptMapById = @@ -671,34 +305,6 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { } } - // Special case for the translate operation with url and without - // conceptMapVersion, find the latest conecptMapVersion - private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { - - Pageable page = PageRequest.of(0, 1); - List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate( - page, theTranslationRequest.getUrl()); - if (!theConceptMapList.isEmpty()) { - return theConceptMapList.get(0).getVersion(); - } - - return null; - } - - private void buildTranslationResult(TranslateConceptResults theTranslationResult) { - - String msg; - if (theTranslationResult.getResults().isEmpty()) { - theTranslationResult.setResult(false); - msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound"); - theTranslationResult.setMessage(msg); - } else { - theTranslationResult.setResult(true); - msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound"); - theTranslationResult.setMessage(msg); - } - } - /** * This method is present only for unit tests, do not call from client code */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java new file mode 100644 index 00000000000..a07e3a229b7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java @@ -0,0 +1,34 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 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.term.api; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; + +/** + * Represents the terminology translate functions + */ +public interface ITermConceptClientMappingSvc extends IValidationSupport { + + TranslateConceptResults translate(TranslationRequest theTranslationRequest); + + TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java index 127861a1961..1fc1b494693 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java @@ -19,17 +19,13 @@ */ package ca.uhn.fhir.jpa.term.api; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.TranslateConceptResults; -import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.hl7.fhir.r4.model.ConceptMap; -public interface ITermConceptMappingSvc extends IValidationSupport { - - TranslateConceptResults translate(TranslationRequest theTranslationRequest); - - TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest); +/** + * Represents ConceptMap translation and persistence operations + */ +public interface ITermConceptMappingSvc extends ITermConceptClientMappingSvc { void deleteConceptMapAndChildren(ResourceTable theResourceTable); From 8a39da4e91f34817b7fd60cb09e37726a17bc58b Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Fri, 10 Nov 2023 13:51:26 -0500 Subject: [PATCH 25/86] - Added logs when MDM candidate search parameters are not defined and candidate search limit is exceeded. (#5446) - fixed extra indentation for example survivorship rule in quickstart.md --- ...ing-extra-logs-to-mdm-candidate-search-parameter.yaml | 4 ++++ .../ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java | 9 ++++++--- .../jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java | 3 ++- .../ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java | 2 +- .../ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java | 5 ++++- 5 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml new file mode 100644 index 00000000000..8c5542712aa --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5445 +title: "Added warnings when MDM candidate search parameters are not defined and candidate search limit is exceeded." diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index fd829d2eb3c..32d51dec50d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -87,9 +87,6 @@ public class MdmMessageHandler implements MessageHandler { if (toProcess) { matchMdmAndUpdateLinks(sourceResource, msg); } - } catch (TooManyCandidatesException e) { - ourLog.error(e.getMessage(), e); - // skip this one with an error message and continue processing } catch (Exception e) { ourLog.error("Failed to handle MDM Matching Resource:", e); throw e; @@ -123,6 +120,12 @@ public class MdmMessageHandler implements MessageHandler { ourLog.trace("Not processing modified message for {}", theMsg.getOperationType()); } } catch (Exception e) { + if (e instanceof TooManyCandidatesException) { + ourLog.debug( + "Failed to handle MDM Matching for resource: {} since candidate matches exceeded the " + + "candidate search limit", + theSourceResource.getIdElement()); + } log(mdmContext, "Failure during MDM processing: " + e.getMessage(), e); mdmContext.addTransactionLogMessage(e.getMessage()); } finally { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java index aafdff442c6..e3a7a5e72d5 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java @@ -159,7 +159,8 @@ public class MdmCandidateSearchSvc { myCandidateSearcher.search(theResourceType, resourceCriteria, theRequestPartitionId); if (!bundleProvider.isPresent()) { throw new TooManyCandidatesException(Msg.code(762) + "More than " + myMdmSettings.getCandidateSearchLimit() - + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching."); + + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching. Updating the " + + "candidate search parameters is strongly recommended for better performance of MDM."); } List resources = bundleProvider.get().getAllResources(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java index f27af6f4e6f..ad3487528f5 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java @@ -149,7 +149,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()); fail(); } catch (TooManyCandidatesException e) { - assertEquals("HAPI-0762: More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching.", e.getMessage()); + assertEquals("HAPI-0762: More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching. Updating the candidate search parameters is strongly recommended for better performance of MDM.", e.getMessage()); } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java index 3189d149783..83340958397 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java @@ -136,7 +136,10 @@ public class MdmRuleValidator implements IMdmRuleValidator { private void validateSearchParams(MdmRulesJson theMdmRulesJson) { ourLog.info("Validating search parameters {}", theMdmRulesJson.getCandidateSearchParams()); - + if (theMdmRulesJson.getCandidateSearchParams().isEmpty()) { + ourLog.warn("No candidate search parameter was found. Defining candidate search parameter is strongly " + + "recommended for better performance of MDM"); + } for (MdmResourceSearchParamJson searchParams : theMdmRulesJson.getCandidateSearchParams()) { searchParams .iterator() From 3bba9fb1f24c6db54b0c02a0123dc28cfabb154f Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 10 Nov 2023 14:54:51 -0500 Subject: [PATCH 26/86] add terminology troubleshooting log (#5439) * init rev * init rev * changelog and additional logging * changelog and additional logging * changelog and additional logging * spotless * review feedback --- .../DefaultProfileValidationSupport.java | 5 ++ .../context/support/IValidationSupport.java | 18 +++++ .../src/main/java/ca/uhn/fhir/util/Logs.java | 7 ++ .../cli/LoadingValidationSupportDstu2.java | 5 ++ .../cli/LoadingValidationSupportDstu3.java | 5 ++ .../5439-terminology-troubleshooting-log.yaml | 4 + ...JpaPersistedResourceValidationSupport.java | 5 ++ .../jpa/term/TermConceptMappingSvcImpl.java | 5 ++ .../CommonCodeSystemsTerminologyService.java | 5 ++ ...ltProfileValidationSupportNpmStrategy.java | 5 ++ ...oryTerminologyServerValidationSupport.java | 5 ++ .../support/LocalFileValidationSupport.java | 5 ++ .../support/NpmPackageValidationSupport.java | 2 +- .../PrePopulatedValidationSupport.java | 5 ++ ...teTerminologyServiceValidationSupport.java | 5 ++ .../SnapshotGeneratingValidationSupport.java | 5 ++ ...ownCodeSystemWarningValidationSupport.java | 5 ++ .../support/ValidationSupportChain.java | 80 ++++++++++++++++++- 18 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java index 4a103500ebf..c90b466982b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java @@ -99,6 +99,11 @@ public class DefaultProfileValidationSupport implements IValidationSupport { } } + @Override + public String getName() { + return myCtx.getVersion().getVersion() + " FHIR Standard Profile Validation Support"; + } + @Override public List fetchAllConformanceResources() { return myDelegate.fetchAllConformanceResources(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index b08d10cadc5..27e0f5ddea5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -409,6 +410,13 @@ public interface IValidationSupport { return null; } + /** + * This field is used by the Terminology Troubleshooting Log to log which validation support module was used for the operation being logged. + */ + default String getName() { + return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support"; + } + enum IssueSeverity { /** * The issue caused the action to fail, and no further checking could be performed. @@ -990,6 +998,16 @@ public interface IValidationSupport { public boolean isReverse() { return myReverse; } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("sourceValueSetUrl", mySourceValueSetUrl) + .append("targetSystemUrl", myTargetSystemUrl) + .append("targetValueSetUrl", myTargetValueSetUrl) + .append("reverse", myReverse) + .toString(); + } } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java index 99a9cf6dee2..2eb220ab03f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java @@ -28,6 +28,9 @@ public class Logs { private static final Logger ourNarrativeGenerationTroubleshootingLog = LoggerFactory.getLogger("ca.uhn.fhir.log.narrative_generation_troubleshooting"); + private static final Logger ourTerminologyTroubleshootingLog = + LoggerFactory.getLogger("ca.uhn.fhir.log.terminology_troubleshooting"); + private static final Logger ourSubscriptionTroubleshootingLog = LoggerFactory.getLogger("ca.cdr.log.subscription_troubleshooting"); @@ -42,6 +45,10 @@ public class Logs { return ourBatchTroubleshootingLog; } + public static Logger getTerminologyTroubleshootingLog() { + return ourTerminologyTroubleshootingLog; + } + public static Logger getSubscriptionTroubleshootingLog() { return ourSubscriptionTroubleshootingLog; } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java index 17c8ff14947..30247c93663 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java @@ -36,6 +36,11 @@ public class LoadingValidationSupportDstu2 implements IValidationSupport { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class); + @Override + public String getName() { + return "Dstu2 CLI Loading Validation Support"; + } + @Override public T fetchResource(Class theClass, String theUri) { String resName = myCtx.getResourceType(theClass); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java index b142733790b..bd308f8766a 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java @@ -35,6 +35,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu3.class); + @Override + public String getName() { + return "Dstu3 CLI Loading Validation Support"; + } + @Override public T fetchResource(Class theClass, String theUri) { String resName = myCtx.getResourceType(theClass); diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml new file mode 100644 index 00000000000..79446e247b9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5439 +title: "Added Terminology Troubleshooting Log to support troubleshooting terminology issues." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java index 7935d32684d..78b1eed3f6d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java @@ -103,6 +103,11 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport myNoMatch = myFhirContext.getResourceDefinition("Basic").newInstance(); } + @Override + public String getName() { + return myFhirContext.getVersion().getVersion() + " JPA Validation Support"; + } + @Override public IBaseResource fetchCodeSystem(String theSystem) { if (TermReadSvcUtil.isLoincUnversionedCodeSystem(theSystem)) { 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 781f5e281ac..603837aada6 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 @@ -69,6 +69,11 @@ public class TermConceptMappingSvcImpl extends TermConceptClientMappingSvcImpl i @Autowired protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao; + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " ConceptMap Validation Support"; + } + @Override @Transactional public void deleteConceptMapAndChildren(ResourceTable theResourceTable) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index 67c114e8cf0..af9cd737566 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -86,6 +86,11 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { myVersionCanonicalizer = new VersionCanonicalizer(theFhirContext); } + @Override + public String getName() { + return myFhirContext.getVersion().getVersion() + " Common Code Systems Validation Support"; + } + @Override public CodeValidationResult validateCodeInValueSet( ValidationSupportContext theValidationSupportContext, diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/DefaultProfileValidationSupportNpmStrategy.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/DefaultProfileValidationSupportNpmStrategy.java index 6fd440f907e..a05af55cd1b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/DefaultProfileValidationSupportNpmStrategy.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/DefaultProfileValidationSupportNpmStrategy.java @@ -39,4 +39,9 @@ public class DefaultProfileValidationSupportNpmStrategy extends NpmPackageValida ourLog.info("Loaded {} Core+Extension resources in {}", countAll(), sw); } + + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " FHIR Standard Profile NPM Validation Support"; + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index 935099d573d..a2a709c0be6 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -72,6 +72,11 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu myVersionCanonicalizer = new VersionCanonicalizer(theCtx); } + @Override + public String getName() { + return myCtx.getVersion().getVersion() + " In-Memory Validation Support"; + } + /** * This setting controls the validation issue severity to report when a code validation * finds that the code is present in the given CodeSystem, but the display name being diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/LocalFileValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/LocalFileValidationSupport.java index c894ff4b685..68ce383b1f8 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/LocalFileValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/LocalFileValidationSupport.java @@ -33,6 +33,11 @@ public class LocalFileValidationSupport extends PrePopulatedValidationSupport { super(ctx); } + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " Local File Validation Support"; + } + @Override public FhirContext getFhirContext() { return myCtx; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java index bc92b604896..f10a525f611 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/NpmPackageValidationSupport.java @@ -18,7 +18,7 @@ import javax.annotation.Nonnull; /** * This interceptor loads and parses FHIR NPM Conformance Packages, and makes the - * artifacts foudn within them available to the FHIR validator. + * artifacts found within them available to the FHIR validator. * * @since 5.5.0 */ diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/PrePopulatedValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/PrePopulatedValidationSupport.java index c5faca542c3..6147a7fc597 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/PrePopulatedValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/PrePopulatedValidationSupport.java @@ -54,6 +54,11 @@ public class PrePopulatedValidationSupport extends BaseStaticResourceValidationS this(theContext, new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>()); } + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " Pre-populated Validation Support"; + } + /** * Constructor * diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index 99a4b1b90c9..a889d36f05e 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -57,6 +57,11 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup myBaseUrl = theBaseUrl; } + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " Remote Terminology Service Validation Support"; + } + @Override public CodeValidationResult validateCode( @Nonnull ValidationSupportContext theValidationSupportContext, diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/SnapshotGeneratingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/SnapshotGeneratingValidationSupport.java index f98c6f8b53b..a4cf3642b0a 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/SnapshotGeneratingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/SnapshotGeneratingValidationSupport.java @@ -179,4 +179,9 @@ public class SnapshotGeneratingValidationSupport implements IValidationSupport { public FhirContext getFhirContext() { return myCtx; } + + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " Snapshot Generating Validation Support"; + } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java index 3d982b604b9..0b4be8d9006 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java @@ -33,6 +33,11 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup super(theFhirContext); } + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " Unknown Code System Warning Validation Support"; + } + @Override public boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) { return true; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 70e7ed9d3bf..2b31d6852d3 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -9,9 +9,11 @@ import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.util.Logs; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.slf4j.Logger; import java.util.ArrayList; import java.util.HashSet; @@ -25,6 +27,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValidationSupportChain implements IValidationSupport { + static Logger ourLog = Logs.getTerminologyTroubleshootingLog(); private List myChain; @@ -66,7 +69,17 @@ public class ValidationSupportChain implements IValidationSupport { retVal.setMessage(translations.getMessage()); } - retVal.getResults().addAll(translations.getResults()); + if (!translations.isEmpty()) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "{} found {} concept translation{} for {}", + next.getName(), + translations.size(), + translations.size() > 1 ? "s" : "", + theRequest); + } + retVal.getResults().addAll(translations.getResults()); + } } } return retVal; @@ -74,6 +87,7 @@ public class ValidationSupportChain implements IValidationSupport { @Override public void invalidateCaches() { + ourLog.debug("Invalidating caches in {} validation support modules", myChain.size()); for (IValidationSupport next : myChain) { next.invalidateCaches(); } @@ -84,6 +98,9 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { boolean retVal = next.isValueSetSupported(theValidationSupportContext, theValueSetUrl); if (retVal) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("ValueSet {} found in {}", theValueSetUrl, next.getName()); + } return true; } } @@ -101,6 +118,9 @@ public class ValidationSupportChain implements IValidationSupport { IBaseResource retVal = next.generateSnapshot(theValidationSupportContext, theInput, theUrl, theWebUrl, theProfileName); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("Profile snapshot for {} generated by {}", theInput.getIdElement(), next.getName()); + } return retVal; } } @@ -178,6 +198,9 @@ public class ValidationSupportChain implements IValidationSupport { ValueSetExpansionOutcome expanded = next.expandValueSet(theValidationSupportContext, theExpansionOptions, theValueSetToExpand); if (expanded != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("ValueSet {} expanded by {}", theValueSetToExpand.getIdElement(), next.getName()); + } return expanded; } } @@ -246,6 +269,13 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { IBaseResource retVal = next.fetchCodeSystem(theSystem); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "CodeSystem {} with System {} fetched by {}", + retVal.getIdElement(), + theSystem, + next.getName()); + } return retVal; } } @@ -257,6 +287,10 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { IBaseResource retVal = next.fetchValueSet(theUrl); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "ValueSet {} with URL {} fetched by {}", retVal.getIdElement(), theUrl, next.getName()); + } return retVal; } } @@ -268,6 +302,10 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { T retVal = next.fetchResource(theClass, theUri); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "Resource {} with URI {} fetched by {}", retVal.getIdElement(), theUri, next.getName()); + } return retVal; } } @@ -279,6 +317,9 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { byte[] retVal = next.fetchBinary(key); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("Binary with key {} fetched by {}", key, next.getName()); + } return retVal; } } @@ -290,6 +331,9 @@ public class ValidationSupportChain implements IValidationSupport { for (IValidationSupport next : myChain) { IBaseResource retVal = next.fetchStructureDefinition(theUrl); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("StructureDefinition with URL {} fetched by {}", theUrl, next.getName()); + } return retVal; } } @@ -300,6 +344,9 @@ public class ValidationSupportChain implements IValidationSupport { public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { for (IValidationSupport next : myChain) { if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) { + if (ourLog.isDebugEnabled()) { + ourLog.debug("CodeSystem with System {} is supported by {}", theSystem, next.getName()); + } return true; } } @@ -321,6 +368,15 @@ public class ValidationSupportChain implements IValidationSupport { CodeValidationResult retVal = next.validateCode( theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "Code {}|{} '{}' in ValueSet {} validated by {}", + theCodeSystem, + theCode, + theDisplay, + theValueSetUrl, + next.getName()); + } return retVal; } } @@ -342,6 +398,15 @@ public class ValidationSupportChain implements IValidationSupport { CodeValidationResult retVal = next.validateCodeInValueSet( theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSet); if (retVal != null) { + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "Code {}|{} '{}' in ValueSet {} validated by {}", + theCodeSystem, + theCode, + theDisplay, + theValueSet.getIdElement(), + next.getName()); + } return retVal; } } @@ -357,7 +422,18 @@ public class ValidationSupportChain implements IValidationSupport { String theDisplayLanguage) { for (IValidationSupport next : myChain) { if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) { - return next.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage); + LookupCodeResult lookupCodeResult = + next.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage); + if (ourLog.isDebugEnabled()) { + ourLog.debug( + "Code {}|{}{} {} by {}", + theSystem, + theCode, + isBlank(theDisplayLanguage) ? "" : " (" + theDisplayLanguage + ")", + lookupCodeResult != null && lookupCodeResult.isFound() ? "found" : "not found", + next.getName()); + } + return lookupCodeResult; } } return null; From 777859ad00f11b6b62e3084fa8e49a40a387b229 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 10 Nov 2023 14:00:08 -0800 Subject: [PATCH 27/86] Rel 6 10 mb (#5441) * Allow cached search with consent active when safe (#5387) Allow the search cache when using consent if safe * Change package installation behaviour such that it updates the existing SearchParameter base with remaining resources (#5376) * Change package installation behavior such that it updates the existing SearchParameter base with remaining resources * Change package installation behavior such that it updates the existing SearchParameter base with remaining resources * Use resourceType in the package installer output to fix tests. Minor change with resourceType condition. Update changelog description to make it more readable. * Use resourceType in the package installer output to fix tests. Minor change with resourceType condition. Update changelog description to make it more readable. * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning (#5389) * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning - Implementation * Reverse Chaining searches returns an error when invoked with parameter _lastUpdated. (#5177) * version bump * Bump to core release 6.0.22 (#5028) * Bump to core release 6.0.16 * Bump to core version 6.0.20 * Fix errors thrown as a result of VersionSpecificWorkerContextWrapper * Bump to core 6.0.22 * Resolve 5126 hfj res ver prov might cause migration error on db that automatically indexes the primary key (#5127) * dropped old index FK_RESVERPROV_RES_PID on RES_PID column before adding IDX_RESVERPROV_RES_PID * added changelog * changed to valid version number * changed to valid version number, need to be ordered by version number... * 5123 - Use DEFAULT partition for server-based requests if none specified (#5124) 5123 - Use DEFAULT partition for server-based requests if none specified * consent remove all suppresses next link in bundle (#5119) * added FIXME with source of issue * added FIXME with root cause * added FIXME with root cause * Providing solution to the issue and removing fixmes. * Providing changelog * auto-formatting. * Adding new test. * Adding a new test for standard paging * let's try this and see if it works...? * fix tests * cleanup to trigger a new run * fixing tests --------- Co-authored-by: Ken Stevens Co-authored-by: peartree * 5117 MDM Score for No Match Fields Should Not Be Included in Total Score (#5118) * fix, test, changelog * fix, test, changelog --------- Co-authored-by: justindar * _source search parameter needs to support modifiers (#5095) _source search parameter needs to support modifiers - added support form :contains, :missing, :above modifiers * Fix HFQL docs (#5151) * Expunge operation on codesystem may throw 500 internal error with precondition fail message. (#5156) * Initial failing test. * Solution with changelog. * fixing format. * Addressing comment from code review. * fixing failing test. --------- Co-authored-by: peartree * documentation update (#5154) Co-authored-by: leif stawnyczy * Fix hsql jdbc driver deps (#5168) Avoid non-included classes in jdbc driver dependencies. * $delete-expunge over 10k resources will now delete all resources (#5144) * First commit with very rough fix and unit test. * Refinements to ResourceIdListStep and Batch2DaoSvcImpl. Make LoadIdsStepTest pass. Enhance Batch2DaoSvcImplTest. * Spotless * Fix checkstyle errors. * Fix test failures. * Minor refactoring. New unit test. Finalize changelist. * Spotless fix. * Delete now useless code from unit test. * Delete more useless code. * Test pre-commit hook * More spotless fixes. * Address most code review feedback. * Remove use of pageSize parameter and see if this breaks the pipeline. * Remove use of pageSize parameter and see if this breaks the pipeline. * Fix the noUrl case by passing an unlimited Pegeable instead. Effectively stop using page size for most databases. * Deprecate the old method and have it call the new one by default. * updating documentation (#5170) Co-authored-by: leif stawnyczy * _source search parameter modifiers for Subscription matching (#5159) * _source search parameter modifiers for Subscription matching - test, implementation and changelog * first fix * tests and preliminary fixes * wip, commit before switching to release branch. * adding capability to handle _lastUpdated in reverse search (_has) * adding changelog * applying spotless. * addressing code review comments. --------- Co-authored-by: tadgh Co-authored-by: dotasek Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: Nathan Doef Co-authored-by: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Co-authored-by: TipzCM Co-authored-by: leif stawnyczy Co-authored-by: michaelabuckley Co-authored-by: Luke deGruchy * Br 20231019 add cr settings for cds hooks (#5394) * Add settings used in CR CDS Services. Remove config dependency on Spring Boot. * Add changelog * Use String.format rather than concat strings * spotless apply * Add javadoc * Upgrade notes for the forced-id change (#5400) Add upgrade notes for forced-id * Clean stale search results more aggressively. (#5396) Use bulk DMA statements when cleaning the search cache. The cleaner job now works as long as possible until a deadline based on the scheduling frequency. * bump version of clinical reasoning (#5406) * Transaction fails if SearchNarrowingInterceptor is registered and Partitioning Enabled - fix cross-tenant requests failure (#5408) * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning - fix and tests added * removed unused alias from SQL query of mdm-clear (#5416) * Issue 5418 support Boolean class return type in BaseInterceptorService (#5421) * Enable child classes to use Boolean class return type * spotless --------- Co-authored-by: juan.marchionatto * If AutoInflateBinaries is enabled, binaries are created on the disk only for the first resource entry of the bundle (#5420) * If AutoInflateBinaries is enabled, binaries created on disk by bundled requests are created only for the first resource entry - fix * Revert "Issue 5418 support Boolean class return type in BaseInterceptorService (#5421)" (#5423) This reverts commit 4e295a59fb143a2ba54bc44305474096e2acd578. Co-authored-by: Nathan Doef * Use new FHIR_ID column for sorting (#5405) * Sort `_id` using new FHIR_ID column. * Fix old tests that put client-assigned ids first. * Better indexing for sort * Bump core to 6.1.2.2 (#5425) * Bump core to 6.1.2.1 Patch release that uses https for primary org.hl7.fhir.core package server * Bump core to 6.1.2.2 * Make sure to return always a value for Boolean class return type. (#5424) Implement change in a non-disruptive way for overriders Co-authored-by: juan.marchionatto * Add non-standard __pid SP for breaking ties cheaply during sorts. (#5428) Add a non-standard __pid SP. * Review changes for new _pid SP. (#5430) Change name to _pid to match our standard and add warning. * Fix VersionCanonicalizer conversion from R5 into DSTU2 for CapabilityStatement, Parameters and StructuredDefinition (#5432) * Fix VersionCanonicalizer conversion from R5 into DSTU2 for CapabilityStatement, Parameters and StructuredDefinition. * Fix spotless issue * CVEs for 6.10.0 (#5433) * Bump jetty * Bump okio-jvm * 8.2.0 mysql connector * Jena and elastic bumps * Fix test * 5412 post bundle on partition incorrect response.link shown (#5413) * Initial fix and unit test provided * spottless check * Made relevant changes to make solution version agnostic * relevant logic changes made * spotless changes made * New logic added to fix failing test cases * formatting * New logic to make the function more robust * spotless checks * Left a trailing slash in the tests * Made relevant test changes and changed logic * spotless changes * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-fullUrl-not-shown-in-response.yaml changing changelog Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> * Formatting requirements --------- Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> * Resolve We don't have guaranteed subscription delivery if a resource is too large (#5414) * first fix * - added the ability to handle null payload to SubscriptionDeliveringMessageSubscriber and SubscriptionDeliveringEmailSubscriber - refactored code to reduce repeated code - cleaned unnecessary comments and reformatted files * Changed myResourceModifiedMessagePersistenceSvc to be autowired * removed unused import * added error handling when inflating the message to email and message subscriber * reformatted code * Fixing subscription tests with mocked IResourceModifiedMessagePersistenceSvc * Changes by gary * Reformatted file * fixed failed tests * implemented test for message and email delivery subscriber. Fixed logical error. Reformatted File. * - implemented IT - fixed logical error - added changelog * fix for cdr tests, NOTE: this makes the assumption that we will always succeed for inflating the database in the tests that uses SynchronousSubscriptionMatcherInterceptor * fix for cdr tests, NOTE: this makes the assumption that we will always succeed for inflating the database in the tests that uses SynchronousSubscriptionMatcherInterceptor * resolve code review comments * reformatted files * fixed tests * Fix for failing IT test in jpaserver-starter (#5435) Co-authored-by: dotasek * wip --------- Co-authored-by: michaelabuckley Co-authored-by: Martha Mitran Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Co-authored-by: dotasek Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: Nathan Doef Co-authored-by: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Co-authored-by: TipzCM Co-authored-by: leif stawnyczy Co-authored-by: Luke deGruchy Co-authored-by: Brenin Rhodes Co-authored-by: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Co-authored-by: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Co-authored-by: juan.marchionatto Co-authored-by: Nathan Doef Co-authored-by: LalithE <132382565+LalithE@users.noreply.github.com> Co-authored-by: dotasek --- .../executor/BaseInterceptorService.java | 18 +- .../java/ca/uhn/fhir/rest/api/Constants.java | 2 + .../canonical/VersionCanonicalizer.java | 6 +- .../canonical/VersionCanonicalizerTest.java | 164 ++++---- .../4803-forced-id-step-2.yaml | 0 ...-lastUpdated-sp-with-reverse-chaining.yaml | 6 + ...rameters-with-multiple-base-resources.yaml | 7 + .../5375-add-cr-settings-for-cds-hooks.yaml | 4 + ...5387-allow-cached-search-with-consent.yaml | 6 + ...s-registered-and-partitioning-enabled.yaml | 6 + .../6_10_0/5395-search-cleaner-faster.yaml | 5 + .../5404-cql-translator-fhirhelpers-bug.yaml | 4 + .../6_10_0/5405-use-new-fhir-id-for-sort.yaml | 4 + ...n-delivery-if-a-resource-is-too-large.yaml | 7 + ...-partition-response-link-is-incorrect.yaml | 4 + .../6_10_0/5415-mdm-clear-fails-on-mssql.yaml | 4 + ...he-first-resource-entry-of-the-bundle.yaml | 6 + .../fhir/changelog/6_10_0/5428-pid-sp.yaml | 5 + ...lizer_fails_capabilitystatement_dstu2.yaml | 5 + .../hapi/fhir/changelog/6_10_0/changes.yaml | 2 +- .../uhn/hapi/fhir/changelog/6_10_0/upgrade.md | 7 + .../fhir/docs/security/consent_interceptor.md | 10 + .../uhn/hapi/fhir/docs/server_jpa/search.md | 6 + .../config/HapiFhirHibernateJpaDialect.java | 2 +- .../ca/uhn/fhir/jpa/config/JpaConfig.java | 7 - .../jpa/dao/data/IMdmLinkJpaRepository.java | 2 +- .../ca/uhn/fhir/jpa/dao/data/ISearchDao.java | 22 +- .../fhir/jpa/dao/data/ISearchIncludeDao.java | 8 +- .../fhir/jpa/dao/data/ISearchResultDao.java | 17 +- .../jpa/dao/data/SearchIdAndResultSize.java | 37 ++ .../ca/uhn/fhir/jpa/entity/SearchResult.java | 9 +- .../tasks/HapiFhirJpaMigrationTasks.java | 13 +- .../jpa/packages/PackageInstallerSvcImpl.java | 268 +++++++------ .../search/StaleSearchDeletingSvcImpl.java | 16 +- .../fhir/jpa/search/builder/QueryStack.java | 256 ++++++++++--- .../jpa/search/builder/SearchBuilder.java | 18 +- .../predicate/ForcedIdPredicateBuilder.java | 51 --- .../ResourceLinkPredicateBuilder.java | 17 +- .../ResourceTablePredicateBuilder.java | 9 +- .../builder/sql/SearchQueryBuilder.java | 38 +- .../search/builder/sql/SqlObjectFactory.java | 5 - .../cache/DatabaseSearchCacheSvcImpl.java | 359 +++++++++++++----- .../jpa/search/cache/ISearchCacheSvc.java | 8 +- ...urceModifiedMessagePersistenceSvcImpl.java | 56 ++- .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 2 +- .../packages/PackageInstallerSvcImplTest.java | 147 +++++-- .../FhirResourceDaoR4SearchLastNAsyncIT.java | 7 +- .../fhir/jpa/model/entity/ResourceTable.java | 12 +- .../jpa/searchparam/ResourceMetaParams.java | 2 + .../BaseSubscriptionDeliverySubscriber.java | 19 + ...SubscriptionDeliveringEmailSubscriber.java | 24 +- ...bscriptionDeliveringMessageSubscriber.java | 16 +- .../SubscriptionActivatingSubscriber.java | 16 +- .../SubscriptionMatchDeliverer.java | 18 +- .../SubscriptionMatchingSubscriber.java | 15 + ...hronousSubscriptionMatcherInterceptor.java | 26 +- .../svc/ResourceModifiedSubmitterSvc.java | 40 +- .../SubscriptionTopicMatchingSubscriber.java | 15 + ...aseSubscriptionDeliverySubscriberTest.java | 53 +++ .../matching/DaoSubscriptionMatcherTest.java | 6 + .../cache/SubscriptionRegistrySharedTest.java | 5 + .../config/TestSubscriptionDstu3Config.java | 6 + ...kingQueueSubscribableChannelDstu3Test.java | 9 + .../SubscriptionMatchingSubscriberTest.java | 8 + .../WebsocketConnectionValidatorTest.java | 6 + .../dao/dstu2/FhirResourceDaoDstu2Test.java | 6 +- .../FhirResourceDaoDstu3SearchNoFtTest.java | 2 +- .../dao/dstu3/FhirResourceDaoDstu3Test.java | 6 +- .../jpa/dao/r4/BasePartitioningR4Test.java | 2 +- .../dao/r4/FhirResourceDaoR4QuerySandbox.java | 150 ++++++++ .../r4/FhirResourceDaoR4SearchNoFtTest.java | 52 +++ .../jpa/dao/r4/FhirResourceDaoR4SortTest.java | 4 +- ...rResourceDaoR4StandardQueriesNoFTTest.java | 60 ++- .../jpa/dao/r4/FhirResourceDaoR4Test.java | 57 --- .../dao/r4/SearchCoordinatorSvcImplTest.java | 31 +- .../PackageInstallerSvcImplCreateTest.java | 9 +- ...ageInstallerSvcImplRewriteHistoryTest.java | 2 +- .../r4/BinaryStorageInterceptorR4Test.java | 92 ++++- ...onsentInterceptorResourceProviderR4IT.java | 155 ++++---- .../provider/r4/MultitenantServerR4Test.java | 54 +++ .../r4/StaleSearchDeletingSvcR4Test.java | 31 +- .../message/MessageSubscriptionR4Test.java | 20 +- .../svc/ResourceModifiedSubmitterSvcTest.java | 10 +- .../ca/uhn/fhir/jpa/dao/TestDaoSearch.java | 28 ++ .../jpa/search/IIdSearchTestTemplate.java | 74 ++++ .../ca/uhn/fhir/jpa/test/BaseJpaTest.java | 5 + .../jpa/test/config/ConnectionWrapper.java | 1 + .../fhir/jpa/config/ConnectionWrapper.java | 1 + .../HapiFhirHibernateJpaDialectTest.java | 4 +- .../fhir/cdshooks/api/ICdsConfigService.java | 4 + .../fhir/cdshooks/config/CdsCrConfig.java | 37 ++ .../fhir/cdshooks/config/CdsHooksConfig.java | 13 +- .../cdshooks/svc/CdsConfigServiceImpl.java | 10 + .../cdshooks/svc/cr/CdsCrServiceDstu3.java | 22 +- .../fhir/cdshooks/svc/cr/CdsCrServiceR4.java | 24 +- .../fhir/cdshooks/svc/cr/CdsCrServiceR5.java | 23 +- .../fhir/cdshooks/svc/cr/CdsCrSettings.java | 44 +++ .../cdshooks/config/TestCdsHooksConfig.java | 4 + .../hapi/fhir/cdshooks/svc/cr/BaseCrTest.java | 2 + .../fhir/cdshooks/svc/cr/TestCrConfig.java | 24 ++ .../svc/cr/resolution/CdsCrServiceR4Test.java | 14 +- .../test/resources/ASLPCrdServiceRequest.json | 7 + .../uhn/fhir/rest/server/RestfulServer.java | 18 +- .../fhir/rest/server/RestfulServerUtils.java | 21 +- .../consent/ConsentInterceptor.java | 80 ++-- .../consent/DelegatingConsentService.java | 6 + .../BaseResourceModifiedMessage.java | 12 +- .../tenant/ITenantIdentificationStrategy.java | 11 +- .../UrlBaseTenantIdentificationStrategy.java | 31 +- .../rest/server/util/ServletRequestUtil.java | 1 + .../rest/server/RestfulServerUtilsTest.java | 44 ++- hapi-fhir-storage-cr/pom.xml | 6 - .../uhn/fhir/cr/config/CrConfigCondition.java | 60 +++ .../uhn/fhir/cr/config/RepositoryConfig.java | 2 - .../cr/config/RepositoryConfigCondition.java | 47 +++ .../cr/config/dstu3/ApplyOperationConfig.java | 20 +- .../cr/config/dstu3/CrProcessorConfig.java | 56 +++ .../config/dstu3/ExtractOperationConfig.java | 13 +- .../config/dstu3/PackageOperationConfig.java | 20 +- .../config/dstu3/PopulateOperationConfig.java | 13 +- .../cr/config/r4/ApplyOperationConfig.java | 20 +- .../fhir/cr/config/r4/CrProcessorConfig.java | 56 +++ .../cr/config/r4/ExtractOperationConfig.java | 13 +- .../cr/config/r4/PackageOperationConfig.java | 20 +- .../cr/config/r4/PopulateOperationConfig.java | 13 +- .../fhir/storage/test/DaoTestDataBuilder.java | 7 +- .../interceptor/BinaryStorageInterceptor.java | 24 +- .../jpa/dao/tx/HapiTransactionService.java | 3 +- .../channel/api/PayloadTooLargeException.java | 35 ++ .../model/ResourceDeliveryMessage.java | 4 + .../model/ResourceModifiedMessage.java | 10 + ...ResourceModifiedMessagePersistenceSvc.java | 24 +- .../ca/uhn/fhir/parser/RDFParserR4Test.java | 6 +- .../interceptor/ConsentInterceptorTest.java | 62 ++- ...lBaseTenantIdentificationStrategyTest.java | 26 ++ pom.xml | 14 +- 136 files changed, 2823 insertions(+), 1017 deletions(-) rename hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/{6_8_0 => 6_10_0}/4803-forced-id-step-2.yaml (100%) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java create mode 100644 hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java create mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java create mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java create mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java create mode 100644 hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java index 07ac7df658b..9a592bdd6e9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java @@ -263,10 +263,14 @@ public abstract class BaseInterceptorService & I return myRegisteredPointcuts.contains(thePointcut); } + protected Class getBooleanReturnType() { + return boolean.class; + } + @Override public boolean callHooks(POINTCUT thePointcut, HookParams theParams) { assert haveAppropriateParams(thePointcut, theParams); - assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == boolean.class; + assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == getBooleanReturnType(); Object retValObj = doCallHooks(thePointcut, theParams, true); return (Boolean) retValObj; @@ -282,14 +286,16 @@ public abstract class BaseInterceptorService & I for (BaseInvoker nextInvoker : invokers) { Object nextOutcome = nextInvoker.invoke(theParams); Class pointcutReturnType = thePointcut.getReturnType(); - if (pointcutReturnType.equals(boolean.class)) { + if (pointcutReturnType.equals(getBooleanReturnType())) { Boolean nextOutcomeAsBoolean = (Boolean) nextOutcome; if (Boolean.FALSE.equals(nextOutcomeAsBoolean)) { ourLog.trace("callHooks({}) for invoker({}) returned false", thePointcut, nextInvoker); theRetVal = false; break; + } else { + theRetVal = true; } - } else if (pointcutReturnType.equals(void.class) == false) { + } else if (!pointcutReturnType.equals(void.class)) { if (nextOutcome != null) { theRetVal = nextOutcome; break; @@ -349,7 +355,7 @@ public abstract class BaseInterceptorService & I List retVal; - if (haveMultiple == false) { + if (!haveMultiple) { // The global list doesn't need to be sorted every time since it's sorted on // insertion each time. Doing so is a waste of cycles.. @@ -485,9 +491,9 @@ public abstract class BaseInterceptorService & I myMethod = theHookMethod; Class returnType = theHookMethod.getReturnType(); - if (myPointcut.getReturnType().equals(boolean.class)) { + if (myPointcut.getReturnType().equals(getBooleanReturnType())) { Validate.isTrue( - boolean.class.equals(returnType) || void.class.equals(returnType), + getBooleanReturnType().equals(returnType) || void.class.equals(returnType), "Method does not return boolean or void: %s", theHookMethod); } else if (myPointcut.getReturnType().equals(void.class)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index 780e528759e..e6301929bfa 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -199,6 +199,8 @@ public class Constants { public static final String PARAM_PRETTY_VALUE_FALSE = "false"; public static final String PARAM_PRETTY_VALUE_TRUE = "true"; public static final String PARAM_PROFILE = "_profile"; + public static final String PARAM_PID = "_pid"; + public static final String PARAM_QUERY = "_query"; public static final String PARAM_RESPONSE_URL = "response-url"; // Used in messaging public static final String PARAM_REVINCLUDE = "_revinclude"; diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java index 16c0af9310e..916322631f9 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java @@ -458,7 +458,7 @@ public class VersionCanonicalizer { @Override public IBaseParameters parametersFromCanonical(Parameters theParameters) { Resource converted = VersionConvertorFactory_10_40.convertResource(theParameters, ADVISOR_10_40); - return (IBaseParameters) reencodeToHl7Org(converted); + return (IBaseParameters) reencodeFromHl7Org(converted); } @Override @@ -470,7 +470,7 @@ public class VersionCanonicalizer { @Override public IBaseResource structureDefinitionFromCanonical(StructureDefinition theResource) { Resource converted = VersionConvertorFactory_10_50.convertResource(theResource, ADVISOR_10_50); - return reencodeToHl7Org(converted); + return reencodeFromHl7Org(converted); } @Override @@ -514,7 +514,7 @@ public class VersionCanonicalizer { @Override public IBaseConformance capabilityStatementFromCanonical(CapabilityStatement theResource) { Resource converted = VersionConvertorFactory_10_50.convertResource(theResource, ADVISOR_10_50); - return (IBaseConformance) reencodeToHl7Org(converted); + return (IBaseConformance) reencodeFromHl7Org(converted); } private Resource reencodeToHl7Org(IBaseResource theInput) { diff --git a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java index bd1e53cba51..c9ff28b1c64 100644 --- a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java +++ b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java @@ -2,22 +2,19 @@ package ca.uhn.hapi.converters.canonical; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.util.HapiExtensions; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.SearchParameter; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; -import java.util.List; import java.util.stream.Collectors; import static ca.uhn.fhir.util.ExtensionUtil.getExtensionPrimitiveValues; @@ -25,73 +22,100 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class VersionCanonicalizerTest { + @Nested + class VersionCanonicalizerR4 { - @Test - public void testToCanonicalCoding() { - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.DSTU2); - IBaseCoding coding = new CodingDt("dstuSystem", "dstuCode"); - Coding convertedCoding = canonicalizer.codingToCanonical(coding); - assertEquals("dstuCode", convertedCoding.getCode()); - assertEquals("dstuSystem", convertedCoding.getSystem()); + private static final FhirVersionEnum FHIR_VERSION = FhirVersionEnum.R4; + private static final VersionCanonicalizer ourCanonicalizer = new VersionCanonicalizer(FHIR_VERSION); + @Test + public void testToCanonical_SearchParameterNoCustomResourceType_ConvertedCorrectly() { + org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); + input.addBase("Patient"); + input.addBase("Observation"); + input.addTarget("Organization"); + + // Test + org.hl7.fhir.r5.model.SearchParameter actual = ourCanonicalizer.searchParameterToCanonical(input); + + // Verify + assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Patient", "Observation")); + assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Organization")); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), empty()); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), empty()); + + } + + @Test + public void testToCanonical_SearchParameterWithCustomResourceType__ConvertedCorrectly() { + // Setup + org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); + input.addBase("Base1"); + input.addBase("Base2"); + input.addTarget("Target1"); + input.addTarget("Target2"); + + // Test + org.hl7.fhir.r5.model.SearchParameter actual = ourCanonicalizer.searchParameterToCanonical(input); + + // Verify + assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); + assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), contains("Base1", "Base2")); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), contains("Target1", "Target2")); + // Original shouldn't be modified + assertThat(input.getBase().stream().map(CodeType::getCode).toList(), contains("Base1", "Base2")); + assertThat(input.getTarget().stream().map(CodeType::getCode).toList(), contains("Target1", "Target2")); + + } } - @Test - public void testFromCanonicalSearchParameter() { - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.DSTU2); + @Nested + class VersionCanonicalizerDstu2 { + private static final FhirVersionEnum FHIR_VERSION = FhirVersionEnum.DSTU2; + private static final VersionCanonicalizer ourCanonicalizer = new VersionCanonicalizer(FHIR_VERSION); - SearchParameter inputR5 = new SearchParameter(); - inputR5.setUrl("http://foo"); - ca.uhn.fhir.model.dstu2.resource.SearchParameter outputDstu2 = (ca.uhn.fhir.model.dstu2.resource.SearchParameter) canonicalizer.searchParameterFromCanonical(inputR5); - assertEquals("http://foo", outputDstu2.getUrl()); + @Test + public void testToCanonical_Coding_ConvertSuccessful() { + IBaseCoding coding = new CodingDt("dstuSystem", "dstuCode"); + Coding convertedCoding = ourCanonicalizer.codingToCanonical(coding); + assertEquals("dstuCode", convertedCoding.getCode()); + assertEquals("dstuSystem", convertedCoding.getSystem()); + } + + @Test + public void testFromCanonical_SearchParameter_ConvertSuccessful() { + SearchParameter inputR5 = new SearchParameter(); + inputR5.setUrl("http://foo"); + ca.uhn.fhir.model.dstu2.resource.SearchParameter outputDstu2 = (ca.uhn.fhir.model.dstu2.resource.SearchParameter) ourCanonicalizer.searchParameterFromCanonical(inputR5); + assertEquals("http://foo", outputDstu2.getUrl()); + } + + @Test + public void testFromCanonical_CapabilityStatement_ConvertSuccessful() { + CapabilityStatement inputR5 = new CapabilityStatement(); + inputR5.setUrl("http://foo"); + Conformance conformance = (Conformance) ourCanonicalizer.capabilityStatementFromCanonical(inputR5); + assertEquals("http://foo", conformance.getUrl()); + } + + @Test + public void testFromCanonical_StructureDefinition_ConvertSuccessful() { + StructureDefinition inputR5 = new StructureDefinition(); + inputR5.setId("123"); + ca.uhn.fhir.model.dstu2.resource.StructureDefinition structureDefinition = (ca.uhn.fhir.model.dstu2.resource.StructureDefinition) ourCanonicalizer.structureDefinitionFromCanonical(inputR5); + assertEquals("StructureDefinition/123", structureDefinition.getId().getValue()); + } + + @Test + public void testFromCanonical_Parameters_ConvertSuccessful() { + org.hl7.fhir.r4.model.Parameters inputR4 = new Parameters(); + inputR4.setParameter("paramA", "1"); + ca.uhn.fhir.model.dstu2.resource.Parameters parameters = (ca.uhn.fhir.model.dstu2.resource.Parameters) ourCanonicalizer.parametersFromCanonical(inputR4); + assertNotNull(parameters.getParameter()); + assertEquals("paramA", parameters.getParameter().get(0).getName()); + } } - - @Test - public void testToCanonicalSearchParameter_NoCustomResourceType() { - // Setup - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.R4); - - org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); - input.addBase("Patient"); - input.addBase("Observation"); - input.addTarget("Organization"); - - // Test - org.hl7.fhir.r5.model.SearchParameter actual = canonicalizer.searchParameterToCanonical(input); - - // Verify - assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Patient", "Observation")); - assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Organization")); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), empty()); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), empty()); - - } - - @Test - public void testToCanonicalSearchParameter_WithCustomResourceType() { - // Setup - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.R4); - - org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); - input.addBase("Base1"); - input.addBase("Base2"); - input.addTarget("Target1"); - input.addTarget("Target2"); - - // Test - org.hl7.fhir.r5.model.SearchParameter actual = canonicalizer.searchParameterToCanonical(input); - - // Verify - assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); - assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), contains("Base1", "Base2")); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), contains("Target1", "Target2")); - // Original shouldn't be modified - assertThat(input.getBase().stream().map(CodeType::getCode).toList(), contains("Base1", "Base2")); - assertThat(input.getTarget().stream().map(CodeType::getCode).toList(), contains("Target1", "Target2")); - - } - - } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4803-forced-id-step-2.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4803-forced-id-step-2.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4803-forced-id-step-2.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml new file mode 100644 index 00000000000..3c050843501 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5176 +jira: SMILE-6333 +title: "Previously, the use of search parameter _lastUpdated as part of a reverse chaining search would return an error + message to the client. This issue has been fixed" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml new file mode 100644 index 00000000000..ba042540a99 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 5366 +jira: SMILE-5184 +title: "The package installer overrides existing (built-in) SearchParameter with multiple base resources. + This is happening when installing US Core package for Practitioner.given as an example. + This change allows the existing SearchParameter to continue to exist with the remaining base resources." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml new file mode 100644 index 00000000000..5ca9d560973 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5375 +title: "Add settings for CDS Services using CDS on FHIR. Also removed the dependency on Spring Boot from the CR configs used by CDS Hooks." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml new file mode 100644 index 00000000000..543467f2dc4 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 5387 +title: "Enable the search cache for some requests even when a consent interceptor is active. + If no consent service uses canSeeResource (i.e. shouldProcessCanSeeResource() returns false); + or startOperation() returns AUTHORIZED; then the search cache is enabled." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml new file mode 100644 index 00000000000..dcfc89bcc74 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5388 +title: "Previously, with partitioning enabled and `UrlBaseTenantIdentificationStrategy` used, registering +`SearchNarrowingInterceptor` would cause to incorrect resolution of `entry.request.url` parameter during +transaction bundle processing. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml new file mode 100644 index 00000000000..871a0b64218 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml @@ -0,0 +1,5 @@ +--- +type: perf +issue: 5395 +title: "The background activity that clears stale search results now has higher throughput. + Busy servers should no longer accumulate dead stale search results." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml new file mode 100644 index 00000000000..a9ee0f693cc --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5404 +title: "Cql translating bug where FHIRHelpers library function was erroring and blocking clinical reasoning content functionality" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml new file mode 100644 index 00000000000..85c322066d1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml @@ -0,0 +1,4 @@ +--- +type: perf +issue: 5405 +title: "Sorting by _id now uses the FHIR_ID column on HFJ_RESOURCE and avoid joins." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml new file mode 100644 index 00000000000..e7f8d7ef0be --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 5407 +title: "Previously, when the payload of a subscription message exceeds the broker maximum message size, exception would +be thrown and retry will be performed indefinitely until the maximum message size is adjusted. Now, the message will be +successfully delivered for rest-hook and email subscriptions, while message subscriptions remains the same behavior as +before." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml new file mode 100644 index 00000000000..03f4b2c8443 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5412 +title: "Previously, with Partitioning enabled, submitting a bundle request would return a response with the partition name displayed twice in `response.link` property. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml new file mode 100644 index 00000000000..d6cfc8cc9da --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5415 +title: "Previously, `$mdm-clear` jobs would fail on MSSQL. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml new file mode 100644 index 00000000000..e1394edddcc --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5419 +title: "Previously, when `AllowAutoInflateBinaries` was enabled in `JpaStorageSettings` and bundles with multiple +resources were submitted, binaries were created on the disk only for the first resource entry of the bundle. +This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml new file mode 100644 index 00000000000..2c91eef4d4e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5428 +title: "Add support for non-standard _pid SearchParameter to the the JPA engine. + This new SP provides an efficient tie-breaking sort key." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml new file mode 100644 index 00000000000..771291f525e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5431 +jira: SMILE-5306 +title: "Previously, using VersionCanonicalizer to convert a CapabilityStatement from R5 to DSTU2 would fail. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml index 86eb2ac6c22..b6ce96f25a1 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml @@ -11,5 +11,5 @@
  • Thymeleaf (Testpage Overlay): 3.0.14.RELEASE -> 3.1.2.RELEASE
  • xpp3 (All): 1.1.4c.0 -> 1.1.6
  • HtmlUnit (All): 2.67.0 -> 2.70.0
  • -
  • org.hl7.fhir.core (All): 6.0.22.2 -> 6.1.2
  • +
  • org.hl7.fhir.core (All): 6.0.22.2 -> 6.1.2.2
  • " diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md index 258d2440c2f..16559ea2e17 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md @@ -1,3 +1,9 @@ +### Major Database Change + +This release makes performance changes to the database definition in a way that is incompatible with releases before 6.4. +Attempting to run version 6.2 or older simultaneously with this release may experience errors when saving new resources. + +### Change Tracking and Subscriptions This release introduces significant a change to the mechanism performing submission of resource modification events to the message broker. Previously, an event would be submitted as part of the synchronous transaction modifying a resource. Synchronous submission yielded responsive publishing with the caveat that events would be dropped @@ -8,6 +14,7 @@ database upon completion of the transaction and subsequently submitted to the br This new asynchronous submission mechanism will introduce a slight delay in event publishing. It is our view that such delay is largely compensated by the capability to retry submission upon failure which will eliminate event losses. +### Tag, Security Label, and Profile changes There are some potentially breaking changes: * On resource retrieval and before storage, tags, security label and profile collections in resource meta will be diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md index 96b2dc481d2..900baa3566d 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md @@ -24,3 +24,13 @@ The ConsentInterceptor requires a user-supplied instance of the [IConsentService ```java {{snippet:classpath:/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java|service}} ``` + +## Performance and Privacy + +Filtering search results in `canSeeResource()` requires inspecting every resource during a search and editing the results. +This is slower than the normal path, and will prevent the reuse of the results from the search cache. +The `willSeeResource()` operation supports reusing cached search results, but removed resources may be 'visible' as holes in returned bundles. +Disabling `canSeeResource()` by returning `false` from `processCanSeeResource()` will enable the search cache. + + + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md index 8699d76d5bf..e760b4a3dab 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md @@ -22,6 +22,12 @@ Searching on Location.Position using `near` currently uses a box search, not a r The special `_filter` is only partially implemented. +### _pid + +The JPA server implements a non-standard special `_pid` which matches/sorts on the raw internal database id. +This sort is useful for imposing tie-breaking sort order in an efficient way. + +Note that this is an internal feature that may change or be removed in the future. Use with caution. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java index 0339ea69546..463e3a471cb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java @@ -96,7 +96,7 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { + makeErrorMessage( messageToPrepend, "resourceIndexedCompositeStringUniqueConstraintFailure")); } - if (constraintName.contains(ResourceTable.IDX_RES_FHIR_ID)) { + if (constraintName.contains(ResourceTable.IDX_RES_TYPE_FHIR_ID)) { throw new ResourceVersionConflictException( Msg.code(825) + makeErrorMessage(messageToPrepend, "forcedIdConstraintFailure")); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 3a44a5f8ed2..c6a27252a95 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -114,7 +114,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -613,12 +612,6 @@ public class JpaConfig { return new DatePredicateBuilder(theSearchBuilder); } - @Bean - @Scope("prototype") - public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchBuilder) { - return new ForcedIdPredicateBuilder(theSearchBuilder); - } - @Bean @Scope("prototype") public NumberPredicateBuilder newNumberPredicateBuilder(SearchQueryBuilder theSearchBuilder) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java index 363f2ae9b28..1962fa63176 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java @@ -53,7 +53,7 @@ public interface IMdmLinkJpaRepository @Modifying @Query( value = - "DELETE FROM MPI_LINK_AUD f WHERE GOLDEN_RESOURCE_PID IN (:goldenPids) OR TARGET_PID IN (:goldenPids)", + "DELETE FROM MPI_LINK_AUD WHERE GOLDEN_RESOURCE_PID IN (:goldenPids) OR TARGET_PID IN (:goldenPids)", nativeQuery = true) void deleteLinksHistoryWithAnyReferenceToPids(@Param("goldenPids") List theResourcePids); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java index 6a9fa7fd5ab..35bb509b69a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.Search; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -30,6 +29,8 @@ import org.springframework.data.repository.query.Param; import java.util.Collection; import java.util.Date; import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; public interface ISearchDao extends JpaRepository, IHapiFhirJpaRepository { @@ -38,10 +39,12 @@ public interface ISearchDao extends JpaRepository, IHapiFhirJpaRep @Query( "SELECT s.myId FROM Search s WHERE (s.myCreated < :cutoff) AND (s.myExpiryOrNull IS NULL OR s.myExpiryOrNull < :now) AND (s.myDeleted IS NULL OR s.myDeleted = FALSE)") - Slice findWhereCreatedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow, Pageable thePage); + Stream findWhereCreatedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow); - @Query("SELECT s.myId FROM Search s WHERE s.myDeleted = TRUE") - Slice findDeleted(Pageable thePage); + @Query("SELECT new ca.uhn.fhir.jpa.dao.data.SearchIdAndResultSize(" + "s.myId, " + + "(select max(sr.myOrder) as maxOrder from SearchResult sr where sr.mySearchPid = s.myId)) " + + "FROM Search s WHERE s.myDeleted = TRUE") + Stream findDeleted(); @Query( "SELECT s FROM Search s WHERE s.myResourceType = :type AND s.mySearchQueryStringHash = :hash AND (s.myCreated > :cutoff) AND s.myDeleted = FALSE AND s.myStatus <> 'FAILED'") @@ -54,10 +57,15 @@ public interface ISearchDao extends JpaRepository, IHapiFhirJpaRep int countDeleted(); @Modifying - @Query("UPDATE Search s SET s.myDeleted = :deleted WHERE s.myId = :pid") - void updateDeleted(@Param("pid") Long thePid, @Param("deleted") boolean theDeleted); + @Query("UPDATE Search s SET s.myDeleted = :deleted WHERE s.myId in (:pids)") + @CanIgnoreReturnValue + int updateDeleted(@Param("pids") Set thePid, @Param("deleted") boolean theDeleted); @Modifying @Query("DELETE FROM Search s WHERE s.myId = :pid") void deleteByPid(@Param("pid") Long theId); + + @Modifying + @Query("DELETE FROM Search s WHERE s.myId in (:pids)") + void deleteByPids(@Param("pids") Collection theSearchToDelete); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java index 9312d300f0a..776b8a94faf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java @@ -20,14 +20,18 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.SearchInclude; +import com.google.errorprone.annotations.CanIgnoreReturnValue; 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 java.util.Collection; + public interface ISearchIncludeDao extends JpaRepository, IHapiFhirJpaRepository { @Modifying - @Query(value = "DELETE FROM SearchInclude r WHERE r.mySearchPid = :search") - void deleteForSearch(@Param("search") Long theSearchPid); + @Query(value = "DELETE FROM SearchInclude r WHERE r.mySearchPid in (:search)") + @CanIgnoreReturnValue + int deleteForSearch(@Param("search") Collection theSearchPid); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java index b16a1d99dbf..eb6a4f89474 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.SearchResult; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -27,6 +28,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.Collection; import java.util.List; public interface ISearchResultDao extends JpaRepository, IHapiFhirJpaRepository { @@ -37,12 +39,19 @@ public interface ISearchResultDao extends JpaRepository, IHa @Query(value = "SELECT r.myResourcePid FROM SearchResult r WHERE r.mySearchPid = :search") List findWithSearchPidOrderIndependent(@Param("search") Long theSearchPid); - @Query(value = "SELECT r.myId FROM SearchResult r WHERE r.mySearchPid = :search") - Slice findForSearch(Pageable thePage, @Param("search") Long theSearchPid); + @Modifying + @Query("DELETE FROM SearchResult s WHERE s.mySearchPid IN :searchIds") + @CanIgnoreReturnValue + int deleteBySearchIds(@Param("searchIds") Collection theSearchIds); @Modifying - @Query("DELETE FROM SearchResult s WHERE s.myId IN :ids") - void deleteByIds(@Param("ids") List theContent); + @Query( + "DELETE FROM SearchResult s WHERE s.mySearchPid = :searchId and s.myOrder >= :rangeStart and s.myOrder <= :rangeEnd") + @CanIgnoreReturnValue + int deleteBySearchIdInRange( + @Param("searchId") Long theSearchId, + @Param("rangeStart") int theRangeStart, + @Param("rangeEnd") int theRangeEnd); @Query("SELECT count(r) FROM SearchResult r WHERE r.mySearchPid = :search") int countForSearch(@Param("search") Long theSearchPid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java new file mode 100644 index 00000000000..8c6d822de7f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java @@ -0,0 +1,37 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 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 java.util.Objects; + +/** + * Record for search result returning the PK of a Search, and the number of associated SearchResults + */ +public class SearchIdAndResultSize { + /** Search PK */ + public final long searchId; + /** Number of SearchResults attached */ + public final int size; + + public SearchIdAndResultSize(long theSearchId, Integer theSize) { + searchId = theSearchId; + size = Objects.requireNonNullElse(theSize, 0); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java index 5dc807554eb..7a559a05988 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java @@ -37,21 +37,22 @@ public class SearchResult implements Serializable { private static final long serialVersionUID = 1L; + @Deprecated(since = "6.10", forRemoval = true) // migrating to composite PK on searchPid,Order @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCH_RES") @SequenceGenerator(name = "SEQ_SEARCH_RES", sequenceName = "SEQ_SEARCH_RES") @Id @Column(name = "PID") private Long myId; - @Column(name = "SEARCH_ORDER", nullable = false, insertable = true, updatable = false) + @Column(name = "SEARCH_PID", insertable = true, updatable = false, nullable = false) + private Long mySearchPid; + + @Column(name = "SEARCH_ORDER", insertable = true, updatable = false, nullable = false) private int myOrder; @Column(name = "RESOURCE_PID", insertable = true, updatable = false, nullable = false) private Long myResourcePid; - @Column(name = "SEARCH_PID", insertable = true, updatable = false, nullable = false) - private Long mySearchPid; - /** * Constructor */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 444549e8968..a0f718dceb8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -118,12 +118,19 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); hfjResource.modifyColumn("20231018.2", "FHIR_ID").nonNullable(); + + hfjResource.dropIndex("20231027.1", "IDX_RES_FHIR_ID"); hfjResource - .addIndex("20231018.3", "IDX_RES_FHIR_ID") + .addIndex("20231027.2", "IDX_RES_TYPE_FHIR_ID") .unique(true) .online(true) - .includeColumns("RES_ID") - .withColumns("FHIR_ID", "RES_TYPE"); + // include res_id and our deleted flag so we can satisfy Observation?_sort=_id from the index on + // platforms that support it. + .includeColumns("RES_ID, RES_DELETED_AT") + .withColumns("RES_TYPE", "FHIR_ID"); + + // For resolving references that don't supply the type. + hfjResource.addIndex("20231027.3", "IDX_RES_FHIR_ID").unique(false).withColumns("FHIR_ID"); } protected void init680() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index 723450da4d0..bc7fe8f5f34 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper; 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.StringParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -64,12 +65,13 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; import javax.annotation.PostConstruct; import static ca.uhn.fhir.jpa.packages.util.PackageUtils.DEFAULT_INSTALL_TYPES; +import static ca.uhn.fhir.util.SearchParameterUtil.getBaseAsStrings; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -251,7 +253,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { for (IBaseResource next : resources) { try { next = isStructureDefinitionWithoutSnapshot(next) ? generateSnapshot(next) : next; - create(next, theInstallationSpec, theOutcome); + install(next, theInstallationSpec, theOutcome); } catch (Exception e) { ourLog.warn( "Failed to upload resource of type {} with ID {} - Error: {}", @@ -345,83 +347,42 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * ============================= Utility methods =============================== */ @VisibleForTesting - void create( + void install( IBaseResource theResource, PackageInstallationSpec theInstallationSpec, PackageInstallOutcomeJson theOutcome) { - IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); - SearchParameterMap map = createSearchParameterMapFor(theResource); - IBundleProvider searchResult = searchResource(dao, map); - if (validForUpload(theResource)) { - if (searchResult.isEmpty()) { - ourLog.info("Creating new resource matching {}", map.toNormalizedQueryString(myFhirContext)); - theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); - - IIdType id = theResource.getIdElement(); - - if (id.isEmpty()) { - createResource(dao, theResource); - ourLog.info("Created resource with new id"); - } else { - if (id.isIdPartValidLong()) { - String newIdPart = "npm-" + id.getIdPart(); - id.setParts(id.getBaseUrl(), id.getResourceType(), newIdPart, id.getVersionIdPart()); - } - - try { - updateResource(dao, theResource); - - ourLog.info("Created resource with existing id"); - } catch (ResourceVersionConflictException exception) { - final Optional optResource = readResourceById(dao, id); - - final String existingResourceUrlOrNull = optResource - .filter(MetadataResource.class::isInstance) - .map(MetadataResource.class::cast) - .map(MetadataResource::getUrl) - .orElse(null); - final String newResourceUrlOrNull = (theResource instanceof MetadataResource) - ? ((MetadataResource) theResource).getUrl() - : null; - - ourLog.error( - "Version conflict error: This is possibly due to a collision between ValueSets from different IGs that are coincidentally using the same resource ID: [{}] and new resource URL: [{}], with the exisitng resource having URL: [{}]. Ignoring this update and continuing: The first IG wins. ", - id.getIdPart(), - newResourceUrlOrNull, - existingResourceUrlOrNull, - exception); - } - } - } else { - if (theInstallationSpec.isReloadExisting()) { - ourLog.info("Updating existing resource matching {}", map.toNormalizedQueryString(myFhirContext)); - theResource.setId(searchResult - .getResources(0, 1) - .get(0) - .getIdElement() - .toUnqualifiedVersionless()); - DaoMethodOutcome outcome = updateResource(dao, theResource); - if (!outcome.isNop()) { - theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); - } - } else { - ourLog.info( - "Skipping update of existing resource matching {}", - map.toNormalizedQueryString(myFhirContext)); - } - } - } else { + if (!validForUpload(theResource)) { ourLog.warn( "Failed to upload resource of type {} with ID {} - Error: Resource failed validation", theResource.fhirType(), theResource.getIdElement().getValue()); + return; + } + + IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); + SearchParameterMap map = createSearchParameterMapFor(theResource); + IBundleProvider searchResult = searchResource(dao, map); + + String resourceQuery = map.toNormalizedQueryString(myFhirContext); + if (!searchResult.isEmpty() && !theInstallationSpec.isReloadExisting()) { + ourLog.info("Skipping update of existing resource matching {}", resourceQuery); + return; + } + if (!searchResult.isEmpty()) { + ourLog.info("Updating existing resource matching {}", resourceQuery); + } + IBaseResource existingResource = + !searchResult.isEmpty() ? searchResult.getResources(0, 1).get(0) : null; + boolean isInstalled = createOrUpdateResource(dao, theResource, existingResource); + if (isInstalled) { + theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); } } private Optional readResourceById(IFhirResourceDao dao, IIdType id) { try { - return Optional.ofNullable(dao.read(id.toUnqualifiedVersionless(), newSystemRequestDetails())); + return Optional.ofNullable(dao.read(id.toUnqualifiedVersionless(), createRequestDetails())); } catch (Exception exception) { // ignore because we're running this query to help build the log @@ -432,30 +393,112 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } private IBundleProvider searchResource(IFhirResourceDao theDao, SearchParameterMap theMap) { - return theDao.search(theMap, newSystemRequestDetails()); + return theDao.search(theMap, createRequestDetails()); } - @Nonnull - private SystemRequestDetails newSystemRequestDetails() { - return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition()); - } + protected boolean createOrUpdateResource( + IFhirResourceDao theDao, IBaseResource theResource, IBaseResource theExistingResource) { + final IIdType id = theResource.getIdElement(); - private void createResource(IFhirResourceDao theDao, IBaseResource theResource) { - if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = newSystemRequestDetails(); - theDao.create(theResource, requestDetails); - } else { - theDao.create(theResource); + if (theExistingResource == null && id.isEmpty()) { + ourLog.debug("Install resource without id will be created"); + theDao.create(theResource, createRequestDetails()); + return true; } + + if (theExistingResource == null && !id.isEmpty() && id.isIdPartValidLong()) { + String newIdPart = "npm-" + id.getIdPart(); + id.setParts(id.getBaseUrl(), id.getResourceType(), newIdPart, id.getVersionIdPart()); + } + + boolean isExistingUpdated = updateExistingResourceIfNecessary(theDao, theResource, theExistingResource); + boolean shouldOverrideId = theExistingResource != null && !isExistingUpdated; + + if (shouldOverrideId) { + ourLog.debug( + "Existing resource {} will be overridden with installed resource {}", + theExistingResource.getIdElement(), + id); + theResource.setId(theExistingResource.getIdElement().toUnqualifiedVersionless()); + } else { + ourLog.debug("Install resource {} will be created", id); + } + + DaoMethodOutcome outcome = updateResource(theDao, theResource); + return outcome != null && !outcome.isNop(); } - DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) { - if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = newSystemRequestDetails(); - return theDao.update(theResource, requestDetails); - } else { - return theDao.update(theResource, new SystemRequestDetails()); + private boolean updateExistingResourceIfNecessary( + IFhirResourceDao theDao, IBaseResource theResource, IBaseResource theExistingResource) { + if (!"SearchParameter".equals(theResource.getClass().getSimpleName())) { + return false; } + if (theExistingResource == null) { + return false; + } + if (theExistingResource + .getIdElement() + .getIdPart() + .equals(theResource.getIdElement().getIdPart())) { + return false; + } + Collection remainingBaseList = new HashSet<>(getBaseAsStrings(myFhirContext, theExistingResource)); + remainingBaseList.removeAll(getBaseAsStrings(myFhirContext, theResource)); + if (remainingBaseList.isEmpty()) { + return false; + } + myFhirContext + .getResourceDefinition(theExistingResource) + .getChildByName("base") + .getMutator() + .setValue(theExistingResource, null); + + for (String baseResourceName : remainingBaseList) { + myFhirContext.newTerser().addElement(theExistingResource, "base", baseResourceName); + } + ourLog.info( + "Existing SearchParameter {} will be updated with base {}", + theExistingResource.getIdElement().getIdPart(), + remainingBaseList); + updateResource(theDao, theExistingResource); + return true; + } + + private DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) { + DaoMethodOutcome outcome = null; + + IIdType id = theResource.getIdElement(); + RequestDetails requestDetails = createRequestDetails(); + + try { + outcome = theDao.update(theResource, requestDetails); + } catch (ResourceVersionConflictException exception) { + final Optional optResource = readResourceById(theDao, id); + + final String existingResourceUrlOrNull = optResource + .filter(MetadataResource.class::isInstance) + .map(MetadataResource.class::cast) + .map(MetadataResource::getUrl) + .orElse(null); + final String newResourceUrlOrNull = + (theResource instanceof MetadataResource) ? ((MetadataResource) theResource).getUrl() : null; + + ourLog.error( + "Version conflict error: This is possibly due to a collision between ValueSets from different IGs that are coincidentally using the same resource ID: [{}] and new resource URL: [{}], with the exisitng resource having URL: [{}]. Ignoring this update and continuing: The first IG wins. ", + id.getIdPart(), + newResourceUrlOrNull, + existingResourceUrlOrNull, + exception); + } + return outcome; + } + + private RequestDetails createRequestDetails() { + SystemRequestDetails requestDetails = new SystemRequestDetails(); + if (myPartitionSettings.isPartitioningEnabled()) { + requestDetails.setRequestPartitionId(RequestPartitionId.defaultPartition()); + } + return requestDetails; } boolean validForUpload(IBaseResource theResource) { @@ -480,7 +523,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { return false; } - if (SearchParameterUtil.getBaseAsStrings(myFhirContext, theResource).isEmpty()) { + if (getBaseAsStrings(myFhirContext, theResource).isEmpty()) { ourLog.warn( "Failed to validate resource of type {} with url {} - Error: Resource base is empty", theResource.fhirType(), @@ -560,20 +603,21 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } - private SearchParameterMap createSearchParameterMapFor(IBaseResource resource) { - if (resource.getClass().getSimpleName().equals("NamingSystem")) { - String uniqueId = extractUniqeIdFromNamingSystem(resource); + private SearchParameterMap createSearchParameterMapFor(IBaseResource theResource) { + String resourceType = theResource.getClass().getSimpleName(); + if ("NamingSystem".equals(resourceType)) { + String uniqueId = extractUniqeIdFromNamingSystem(theResource); return SearchParameterMap.newSynchronous().add("value", new StringParam(uniqueId).setExact(true)); - } else if (resource.getClass().getSimpleName().equals("Subscription")) { - String id = extractIdFromSubscription(resource); + } else if ("Subscription".equals(resourceType)) { + String id = extractSimpleValue(theResource, "id"); return SearchParameterMap.newSynchronous().add("_id", new TokenParam(id)); - } else if (resource.getClass().getSimpleName().equals("SearchParameter")) { - return buildSearchParameterMapForSearchParameter(resource); - } else if (resourceHasUrlElement(resource)) { - String url = extractUniqueUrlFromMetadataResource(resource); + } else if ("SearchParameter".equals(resourceType)) { + return buildSearchParameterMapForSearchParameter(theResource); + } else if (resourceHasUrlElement(theResource)) { + String url = extractSimpleValue(theResource, "url"); return SearchParameterMap.newSynchronous().add("url", new UriParam(url)); } else { - TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(resource); + TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(theResource); return SearchParameterMap.newSynchronous().add("identifier", identifierToken); } } @@ -593,7 +637,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } if (resourceHasUrlElement(theResource)) { - String url = extractUniqueUrlFromMetadataResource(theResource); + String url = extractSimpleValue(theResource, "url"); return SearchParameterMap.newSynchronous().add("url", new UriParam(url)); } else { TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(theResource); @@ -601,32 +645,17 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } - private String extractUniqeIdFromNamingSystem(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IBase uniqueIdComponent = (IBase) terser.getSingleValueOrNull(resource, "uniqueId"); + private String extractUniqeIdFromNamingSystem(IBaseResource theResource) { + IBase uniqueIdComponent = (IBase) extractValue(theResource, "uniqueId"); if (uniqueIdComponent == null) { throw new ImplementationGuideInstallationException( Msg.code(1291) + "NamingSystem does not have uniqueId component."); } - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(uniqueIdComponent, "value"); - return (String) asPrimitiveType.getValue(); + return extractSimpleValue(uniqueIdComponent, "value"); } - private String extractIdFromSubscription(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(resource, "id"); - return (String) asPrimitiveType.getValue(); - } - - private String extractUniqueUrlFromMetadataResource(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(resource, "url"); - return (String) asPrimitiveType.getValue(); - } - - private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - Identifier identifier = (Identifier) terser.getSingleValueOrNull(resource, "identifier"); + private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource theResource) { + Identifier identifier = (Identifier) extractValue(theResource, "identifier"); if (identifier != null) { return new TokenParam(identifier.getSystem(), identifier.getValue()); } else { @@ -635,6 +664,15 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } + private Object extractValue(IBase theResource, String thePath) { + return myFhirContext.newTerser().getSingleValueOrNull(theResource, thePath); + } + + private String extractSimpleValue(IBase theResource, String thePath) { + IPrimitiveType asPrimitiveType = (IPrimitiveType) extractValue(theResource, thePath); + return (String) asPrimitiveType.getValue(); + } + private boolean resourceHasUrlElement(IBaseResource resource) { BaseRuntimeElementDefinition def = myFhirContext.getElementDefinition(resource.getClass()); if (!(def instanceof BaseRuntimeElementCompositeDefinition)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java index 700dfa9484e..b37b0be204d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java @@ -25,12 +25,16 @@ import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; +import ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl; import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc; import org.quartz.JobExecutionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS; /** @@ -42,7 +46,6 @@ import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLE // in Smile. // public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHasScheduledJobs { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class); @Autowired private JpaStorageSettings myStorageSettings; @@ -53,7 +56,16 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHas @Override @Transactional(propagation = Propagation.NEVER) public void pollForStaleSearchesAndDeleteThem() { - mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); + mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions(), getDeadline()); + } + + /** + * Calculate a deadline to finish before the next scheduled run. + */ + protected Instant getDeadline() { + return Instant.ofEpochMilli(DatabaseSearchCacheSvcImpl.now()) + // target a 90% duty-cycle to avoid confusing quartz + .plus((long) (SEARCH_CLEANUP_JOB_INTERVAL_MILLIS * 0.90), ChronoUnit.MILLIS); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 16ed33dd153..1075761f352 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -43,7 +43,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ICanMakeMissingParamPredicate; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ParsedLocationParam; @@ -69,10 +68,10 @@ import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.api.SearchContainedModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.QuantityParam; @@ -95,7 +94,6 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.Expression; import com.healthmarketscience.sqlbuilder.InCondition; -import com.healthmarketscience.sqlbuilder.OrderObject; import com.healthmarketscience.sqlbuilder.SelectQuery; import com.healthmarketscience.sqlbuilder.SetOperationQuery; import com.healthmarketscience.sqlbuilder.Subquery; @@ -123,6 +121,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nullable; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.fromOperation; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.getChainedPart; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.getParamNameWithPrefix; @@ -275,16 +274,21 @@ public class QueryStack { } public void addSortOnResourceId(boolean theAscending) { + ResourceTablePredicateBuilder resourceTablePredicateBuilder; BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); - ForcedIdPredicateBuilder sortPredicateBuilder = - mySqlBuilder.addForcedIdPredicateBuilder(firstPredicateBuilder.getResourceIdColumn()); - if (!theAscending) { - mySqlBuilder.addSortString( - sortPredicateBuilder.getColumnForcedId(), false, OrderObject.NullOrder.FIRST, myUseAggregate); + if (firstPredicateBuilder instanceof ResourceTablePredicateBuilder) { + resourceTablePredicateBuilder = (ResourceTablePredicateBuilder) firstPredicateBuilder; } else { - mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), true, myUseAggregate); + resourceTablePredicateBuilder = + mySqlBuilder.addResourceTablePredicateBuilder(firstPredicateBuilder.getResourceIdColumn()); } - mySqlBuilder.addSortNumeric(firstPredicateBuilder.getResourceIdColumn(), theAscending, myUseAggregate); + mySqlBuilder.addSortString(resourceTablePredicateBuilder.getColumnFhirId(), theAscending, myUseAggregate); + } + + /** Sort on RES_ID -- used to break ties for reliable sort */ + public void addSortOnResourcePID(boolean theAscending) { + BaseJoiningPredicateBuilder predicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); + mySqlBuilder.addSortString(predicateBuilder.getResourceIdColumn(), theAscending); } public void addSortOnResourceLink( @@ -1107,7 +1111,7 @@ public class QueryStack { if (paramName.startsWith("_has:")) { - ourLog.trace("Handing double _has query: {}", paramName); + ourLog.trace("Handling double _has query: {}", paramName); String qualifier = paramName.substring(4); for (IQueryParameterType next : nextOrList) { @@ -1160,26 +1164,30 @@ public class QueryStack { parameterName = parameterName.substring(0, colonIndex); } - ResourceLinkPredicateBuilder join = + ResourceLinkPredicateBuilder resourceLinkTableJoin = mySqlBuilder.addReferencePredicateBuilderReversed(this, theSourceJoinColumn); - Condition partitionPredicate = join.createPartitionIdPredicate(theRequestPartitionId); + Condition partitionPredicate = resourceLinkTableJoin.createPartitionIdPredicate(theRequestPartitionId); - List paths = join.createResourceLinkPaths(targetResourceType, paramReference, new ArrayList<>()); + List paths = resourceLinkTableJoin.createResourceLinkPaths( + targetResourceType, paramReference, new ArrayList<>()); if (CollectionUtils.isEmpty(paths)) { throw new InvalidRequestException(Msg.code(2305) + "Reference field does not exist: " + paramReference); } + Condition typePredicate = BinaryCondition.equalTo( - join.getColumnTargetResourceType(), mySqlBuilder.generatePlaceholder(theResourceType)); - Condition pathPredicate = - toEqualToOrInPredicate(join.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths)); - Condition linkedPredicate = searchForIdsWithAndOr( - join.getColumnSrcResourceId(), - targetResourceType, - parameterName, - Collections.singletonList(orValues), - theRequest, - theRequestPartitionId, - SearchContainedModeEnum.FALSE); + resourceLinkTableJoin.getColumnTargetResourceType(), + mySqlBuilder.generatePlaceholder(theResourceType)); + Condition pathPredicate = toEqualToOrInPredicate( + resourceLinkTableJoin.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths)); + + Condition linkedPredicate = + searchForIdsWithAndOr(with().setSourceJoinColumn(resourceLinkTableJoin.getColumnSrcResourceId()) + .setResourceName(targetResourceType) + .setParamName(parameterName) + .setAndOrParams(Collections.singletonList(orValues)) + .setRequest(theRequest) + .setRequestPartitionId(theRequestPartitionId)); + andPredicates.add(toAndPredicate(partitionPredicate, pathPredicate, typePredicate, linkedPredicate)); } @@ -2270,57 +2278,125 @@ public class QueryStack { } @Nullable - public Condition searchForIdsWithAndOr( - @Nullable DbColumn theSourceJoinColumn, - String theResourceName, - String theParamName, - List> theAndOrParams, - RequestDetails theRequest, - RequestPartitionId theRequestPartitionId, - SearchContainedModeEnum theSearchContainedMode) { + public Condition searchForIdsWithAndOr(SearchForIdsParams theSearchForIdsParams) { - if (theAndOrParams.isEmpty()) { + if (theSearchForIdsParams.myAndOrParams.isEmpty()) { return null; } - switch (theParamName) { + switch (theSearchForIdsParams.myParamName) { case IAnyResource.SP_RES_ID: return createPredicateResourceId( - theSourceJoinColumn, theAndOrParams, theResourceName, null, theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myResourceName, + null, + theSearchForIdsParams.myRequestPartitionId); + + case Constants.PARAM_PID: + return createPredicateResourcePID( + theSearchForIdsParams.mySourceJoinColumn, theSearchForIdsParams.myAndOrParams); case PARAM_HAS: return createPredicateHas( - theSourceJoinColumn, theResourceName, theAndOrParams, theRequest, theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); case Constants.PARAM_TAG: case Constants.PARAM_PROFILE: case Constants.PARAM_SECURITY: if (myStorageSettings.getTagStorageMode() == JpaStorageSettings.TagStorageModeEnum.INLINE) { return createPredicateSearchParameter( - theSourceJoinColumn, - theResourceName, - theParamName, - theAndOrParams, - theRequest, - theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); } else { - return createPredicateTag(theSourceJoinColumn, theAndOrParams, theParamName, theRequestPartitionId); + return createPredicateTag( + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myRequestPartitionId); } case Constants.PARAM_SOURCE: - return createPredicateSourceForAndList(theSourceJoinColumn, theAndOrParams); + return createPredicateSourceForAndList( + theSearchForIdsParams.mySourceJoinColumn, theSearchForIdsParams.myAndOrParams); + + case Constants.PARAM_LASTUPDATED: + // this case statement handles a _lastUpdated query as part of a reverse search + // only (/Patient?_has:Encounter:patient:_lastUpdated=ge2023-10-24). + // performing a _lastUpdated query on a resource (/Patient?_lastUpdated=eq2023-10-24) + // is handled in {@link SearchBuilder#createChunkedQuery}. + return createReverseSearchPredicateLastUpdated( + theSearchForIdsParams.myAndOrParams, theSearchForIdsParams.mySourceJoinColumn); default: return createPredicateSearchParameter( - theSourceJoinColumn, - theResourceName, - theParamName, - theAndOrParams, - theRequest, - theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); } } + /** + * Raw match on RES_ID + */ + private Condition createPredicateResourcePID( + DbColumn theSourceJoinColumn, List> theAndOrParams) { + + DbColumn pidColumn = theSourceJoinColumn; + + if (pidColumn == null) { + BaseJoiningPredicateBuilder predicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); + pidColumn = predicateBuilder.getResourceIdColumn(); + } + + // we don't support any modifiers for now + Set pids = theAndOrParams.stream() + .map(orList -> orList.stream() + .map(v -> v.getValueAsQueryToken(myFhirContext)) + .map(Long::valueOf) + .collect(Collectors.toSet())) + .reduce(Sets::intersection) + .orElse(Set.of()); + + if (pids.isEmpty()) { + mySqlBuilder.setMatchNothing(); + return null; + } + + return toEqualToOrInPredicate(pidColumn, mySqlBuilder.generatePlaceholders(pids)); + } + + private Condition createReverseSearchPredicateLastUpdated( + List> theAndOrParams, DbColumn theSourceColumn) { + + ResourceTablePredicateBuilder resourceTableJoin = + mySqlBuilder.addResourceTablePredicateBuilder(theSourceColumn); + + List andPredicates = new ArrayList<>(theAndOrParams.size()); + + for (List aList : theAndOrParams) { + if (!aList.isEmpty()) { + DateParam dateParam = (DateParam) aList.get(0); + DateRangeParam dateRangeParam = new DateRangeParam(dateParam); + Condition aCondition = mySqlBuilder.addPredicateLastUpdated(dateRangeParam, resourceTableJoin); + andPredicates.add(aCondition); + } + } + + return toAndPredicate(andPredicates); + } + @Nullable private Condition createPredicateSearchParameter( @Nullable DbColumn theSourceJoinColumn, @@ -3020,4 +3096,82 @@ public class QueryStack { theParamDefinition, myOrValues, myLeafTarget, myLeafParamName, myLeafPathPrefix, myQualifiers); } } + + public static class SearchForIdsParams { + DbColumn mySourceJoinColumn; + String myResourceName; + String myParamName; + List> myAndOrParams; + RequestDetails myRequest; + RequestPartitionId myRequestPartitionId; + ResourceTablePredicateBuilder myResourceTablePredicateBuilder; + + public static SearchForIdsParams with() { + return new SearchForIdsParams(); + } + + public DbColumn getSourceJoinColumn() { + return mySourceJoinColumn; + } + + public SearchForIdsParams setSourceJoinColumn(DbColumn theSourceJoinColumn) { + mySourceJoinColumn = theSourceJoinColumn; + return this; + } + + public String getResourceName() { + return myResourceName; + } + + public SearchForIdsParams setResourceName(String theResourceName) { + myResourceName = theResourceName; + return this; + } + + public String getParamName() { + return myParamName; + } + + public SearchForIdsParams setParamName(String theParamName) { + myParamName = theParamName; + return this; + } + + public List> getAndOrParams() { + return myAndOrParams; + } + + public SearchForIdsParams setAndOrParams(List> theAndOrParams) { + myAndOrParams = theAndOrParams; + return this; + } + + public RequestDetails getRequest() { + return myRequest; + } + + public SearchForIdsParams setRequest(RequestDetails theRequest) { + myRequest = theRequest; + return this; + } + + public RequestPartitionId getRequestPartitionId() { + return myRequestPartitionId; + } + + public SearchForIdsParams setRequestPartitionId(RequestPartitionId theRequestPartitionId) { + myRequestPartitionId = theRequestPartitionId; + return this; + } + + public ResourceTablePredicateBuilder getResourceTablePredicateBuilder() { + return myResourceTablePredicateBuilder; + } + + public SearchForIdsParams setResourceTablePredicateBuilder( + ResourceTablePredicateBuilder theResourceTablePredicateBuilder) { + myResourceTablePredicateBuilder = theResourceTablePredicateBuilder; + return this; + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index 60d0a986031..a268f5f0d84 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -131,6 +131,7 @@ import javax.persistence.criteria.CriteriaBuilder; import static ca.uhn.fhir.jpa.model.util.JpaConstants.UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE; import static ca.uhn.fhir.jpa.search.builder.QueryStack.LOCATION_POSITION; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -281,14 +282,11 @@ public class SearchBuilder implements ISearchBuilder { continue; } List> andOrParams = myParams.get(nextParamName); - Condition predicate = theQueryStack.searchForIdsWithAndOr( - null, - myResourceName, - nextParamName, - andOrParams, - theRequest, - myRequestPartitionId, - searchContainedMode); + Condition predicate = theQueryStack.searchForIdsWithAndOr(with().setResourceName(myResourceName) + .setParamName(nextParamName) + .setAndOrParams(andOrParams) + .setRequest(theRequest) + .setRequestPartitionId(myRequestPartitionId)); if (predicate != null) { theSearchSqlBuilder.addPredicate(predicate); } @@ -840,6 +838,10 @@ public class SearchBuilder implements ISearchBuilder { theQueryStack.addSortOnResourceId(ascending); + } else if (Constants.PARAM_PID.equals(theSort.getParamName())) { + + theQueryStack.addSortOnResourcePID(ascending); + } else if (Constants.PARAM_LASTUPDATED.equals(theSort.getParamName())) { theQueryStack.addSortOnLastUpdated(ascending); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java deleted file mode 100644 index 6f8d1e1b66c..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2023 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.search.builder.predicate; - -import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; -import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ForcedIdPredicateBuilder extends BaseJoiningPredicateBuilder { - - private static final Logger ourLog = LoggerFactory.getLogger(ForcedIdPredicateBuilder.class); - private final DbColumn myColumnResourceId; - private final DbColumn myColumnForcedId; - - /** - * Constructor - */ - public ForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { - super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_FORCED_ID")); - - myColumnResourceId = getTable().addColumn("RESOURCE_PID"); - myColumnForcedId = getTable().addColumn("FORCED_ID"); - } - - @Override - public DbColumn getResourceIdColumn() { - return myColumnResourceId; - } - - public DbColumn getColumnForcedId() { - return myColumnForcedId; - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java index 5952f398c56..81b42f46429 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java @@ -51,7 +51,6 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.api.SearchContainedModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -87,6 +86,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -456,14 +456,13 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im List andPredicates = new ArrayList<>(); List> chainParamValues = Collections.singletonList(orValues); - andPredicates.add(childQueryFactory.searchForIdsWithAndOr( - myColumnTargetResourceId, - subResourceName, - chain, - chainParamValues, - theRequest, - theRequestPartitionId, - SearchContainedModeEnum.FALSE)); + andPredicates.add( + childQueryFactory.searchForIdsWithAndOr(with().setSourceJoinColumn(myColumnTargetResourceId) + .setResourceName(subResourceName) + .setParamName(chain) + .setAndOrParams(chainParamValues) + .setRequest(theRequest) + .setRequestPartitionId(theRequestPartitionId))); orPredicates.add(QueryParameterUtils.toAndPredicate(andPredicates)); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java index c9fb8845dd7..75ccddb1170 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.search.builder.predicate; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; import ca.uhn.fhir.jpa.util.QueryParameterUtils; import com.healthmarketscience.sqlbuilder.BinaryCondition; @@ -35,6 +36,7 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { private final DbColumn myColumnResType; private final DbColumn myColumnLastUpdated; private final DbColumn myColumnLanguage; + private final DbColumn myColumnFhirId; /** * Constructor @@ -42,10 +44,11 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { public ResourceTablePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RESOURCE")); myColumnResId = getTable().addColumn("RES_ID"); - myColumnResType = getTable().addColumn("RES_TYPE"); + myColumnResType = getTable().addColumn(ResourceTable.RES_TYPE); myColumnResDeletedAt = getTable().addColumn("RES_DELETED_AT"); myColumnLastUpdated = getTable().addColumn("RES_UPDATED"); myColumnLanguage = getTable().addColumn("RES_LANGUAGE"); + myColumnFhirId = getTable().addColumn(ResourceTable.FHIR_ID); } @Override @@ -77,4 +80,8 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { public DbColumn getColumnLastUpdated() { return myColumnLastUpdated; } + + public DbColumn getColumnFhirId() { + return myColumnFhirId; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java index cb1d3a74bd4..e840bdec9d3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -62,7 +61,6 @@ import com.healthmarketscience.sqlbuilder.dbspec.basic.DbJoin; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSchema; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSpec; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; -import org.apache.commons.lang3.Validate; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.pagination.AbstractLimitHandler; @@ -222,18 +220,6 @@ public class SearchQueryBuilder { return mySqlBuilderFactory.dateIndexTable(this); } - /** - * Add and return a predicate builder for selecting a forced ID. This is only intended for use with sorts so it can not - * be the root query. - */ - public ForcedIdPredicateBuilder addForcedIdPredicateBuilder(@Nonnull DbColumn theSourceJoinColumn) { - Validate.isTrue(theSourceJoinColumn != null); - - ForcedIdPredicateBuilder retVal = mySqlBuilderFactory.newForcedIdPredicateBuilder(this); - addTableForSorting(retVal, theSourceJoinColumn); - return retVal; - } - /** * Create, add and return a predicate builder (or a root query if no root query exists yet) for selecting on a NUMBER search parameter */ @@ -417,11 +403,6 @@ public class SearchQueryBuilder { addTable(thePredicateBuilder, theSourceJoinColumn, SelectQuery.JoinType.INNER); } - private void addTableForSorting( - BaseJoiningPredicateBuilder thePredicateBuilder, @Nullable DbColumn theSourceJoinColumn) { - addTable(thePredicateBuilder, theSourceJoinColumn, SelectQuery.JoinType.LEFT_OUTER); - } - private void addTable( BaseJoiningPredicateBuilder thePredicateBuilder, @Nullable DbColumn theSourceJoinColumn, @@ -699,15 +680,24 @@ public class SearchQueryBuilder { public ComboCondition addPredicateLastUpdated(DateRangeParam theDateRange) { ResourceTablePredicateBuilder resourceTableRoot = getOrCreateResourceTablePredicateBuilder(false); + return addPredicateLastUpdated(theDateRange, resourceTableRoot); + } + + public ComboCondition addPredicateLastUpdated( + DateRangeParam theDateRange, ResourceTablePredicateBuilder theResourceTablePredicateBuilder) { List conditions = new ArrayList<>(2); BinaryCondition condition; if (isNotEqualsComparator(theDateRange)) { condition = createConditionForValueWithComparator( - LESSTHAN, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getLowerBoundAsInstant()); + LESSTHAN, + theResourceTablePredicateBuilder.getLastUpdatedColumn(), + theDateRange.getLowerBoundAsInstant()); conditions.add(condition); condition = createConditionForValueWithComparator( - GREATERTHAN, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getUpperBoundAsInstant()); + GREATERTHAN, + theResourceTablePredicateBuilder.getLastUpdatedColumn(), + theDateRange.getUpperBoundAsInstant()); conditions.add(condition); return ComboCondition.or(conditions.toArray(new Condition[0])); } @@ -715,7 +705,7 @@ public class SearchQueryBuilder { if (theDateRange.getLowerBoundAsInstant() != null) { condition = createConditionForValueWithComparator( GREATERTHAN_OR_EQUALS, - resourceTableRoot.getLastUpdatedColumn(), + theResourceTablePredicateBuilder.getLastUpdatedColumn(), theDateRange.getLowerBoundAsInstant()); conditions.add(condition); } @@ -723,7 +713,7 @@ public class SearchQueryBuilder { if (theDateRange.getUpperBoundAsInstant() != null) { condition = createConditionForValueWithComparator( LESSTHAN_OR_EQUALS, - resourceTableRoot.getLastUpdatedColumn(), + theResourceTablePredicateBuilder.getLastUpdatedColumn(), theDateRange.getUpperBoundAsInstant()); conditions.add(condition); } @@ -757,7 +747,7 @@ public class SearchQueryBuilder { List excludePids = JpaPid.toLongList(theExistingPidSetToExclude); - ourLog.trace("excludePids = " + excludePids); + ourLog.trace("excludePids = {}", excludePids); DbColumn resourceIdColumn = getOrCreateFirstPredicateBuilder().getResourceIdColumn(); InCondition predicate = new InCondition(resourceIdColumn, generatePlaceholders(excludePids)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java index 29e04527f96..621fe211185 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -63,10 +62,6 @@ public class SqlObjectFactory { return myApplicationContext.getBean(DatePredicateBuilder.class, theSearchSqlBuilder); } - public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { - return myApplicationContext.getBean(ForcedIdPredicateBuilder.class, theSearchSqlBuilder); - } - public NumberPredicateBuilder numberIndexTable(SearchQueryBuilder theSearchSqlBuilder) { return myApplicationContext.getBean(NumberPredicateBuilder.class, theSearchSqlBuilder); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java index 7bf60372f0a..0f666f60505 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java @@ -25,29 +25,35 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; +import ca.uhn.fhir.jpa.dao.data.SearchIdAndResultSize; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService.IExecutionBuilder; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.Session; import org.hl7.fhir.dstu3.model.InstantType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.sql.Connection; import java.time.Instant; import java.util.Collection; import java.util.Date; -import java.util.List; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.persistence.EntityManager; public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { /* @@ -56,13 +62,12 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { * type query and this can fail if we have 1000s of params */ public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT = 500; - public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 20000; + public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 50000; public static final long SEARCH_CLEANUP_JOB_INTERVAL_MILLIS = DateUtils.MILLIS_PER_MINUTE; public static final int DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND = 2000; private static final Logger ourLog = LoggerFactory.getLogger(DatabaseSearchCacheSvcImpl.class); private static int ourMaximumResultsToDeleteInOneStatement = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT; - private static int ourMaximumResultsToDeleteInOnePass = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS; - private static int ourMaximumSearchesToCheckForDeletionCandidacy = DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND; + private static int ourMaximumResultsToDeleteInOneCommit = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS; private static Long ourNowForUnitTests; /* * We give a bit of extra leeway just to avoid race conditions where a query result @@ -74,6 +79,9 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { @Autowired private ISearchDao mySearchDao; + @Autowired + private EntityManager myEntityManager; + @Autowired private ISearchResultDao mySearchResultDao; @@ -169,14 +177,249 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { return Optional.empty(); } + /** + * A transient worker for a single pass through stale-search deletion. + */ + class DeleteRun { + final RequestPartitionId myRequestPartitionId; + final Instant myDeadline; + final Date myCutoffForDeletion; + final Set myUpdateDeletedFlagBatch = new HashSet<>(); + final Set myDeleteSearchBatch = new HashSet<>(); + /** the Search pids of the SearchResults we plan to delete in a chunk */ + final Set myDeleteSearchResultsBatch = new HashSet<>(); + /** + * Number of results we have queued up in mySearchPidsToDeleteResults to delete. + * We try to keep this to a reasonable size to avoid long transactions that may escalate to a table lock. + */ + private int myDeleteSearchResultsBatchCount = 0; + + DeleteRun(Instant theDeadline, Date theCutoffForDeletion, RequestPartitionId theRequestPartitionId) { + myDeadline = theDeadline; + myCutoffForDeletion = theCutoffForDeletion; + myRequestPartitionId = theRequestPartitionId; + } + + /** + * Mark all ids in the mySearchesToMarkForDeletion buffer as deleted, and clear the buffer. + */ + public void flushDeleteMarks() { + if (myUpdateDeletedFlagBatch.isEmpty()) { + return; + } + ourLog.debug("Marking {} searches as deleted", myUpdateDeletedFlagBatch.size()); + mySearchDao.updateDeleted(myUpdateDeletedFlagBatch, true); + myUpdateDeletedFlagBatch.clear(); + commitOpenChanges(); + } + + /** + * Dig into the guts of our Hibernate session, flush any changes in the session, and commit the underlying connection. + */ + private void commitOpenChanges() { + // flush to force Hibernate to actually get a connection from the pool + myEntityManager.flush(); + // get our connection from the underlying Hibernate session, and commit + //noinspection resource + myEntityManager.unwrap(Session.class).doWork(Connection::commit); + } + + void throwIfDeadlineExpired() { + boolean result = Instant.ofEpochMilli(now()).isAfter(myDeadline); + if (result) { + throw new DeadlineException( + Msg.code(2443) + "Deadline expired while cleaning Search cache - " + myDeadline); + } + } + + private int deleteMarkedSearchesInBatches() { + AtomicInteger deletedCounter = new AtomicInteger(0); + + try (final Stream toDelete = mySearchDao.findDeleted()) { + assert toDelete != null; + + toDelete.forEach(nextSearchToDelete -> { + throwIfDeadlineExpired(); + + deleteSearchAndResults(nextSearchToDelete.searchId, nextSearchToDelete.size); + + deletedCounter.incrementAndGet(); + }); + } + + // flush anything left in the buffers + flushSearchResultDeletes(); + flushSearchAndIncludeDeletes(); + + int deletedCount = deletedCounter.get(); + + ourLog.info("Deleted {} expired searches", deletedCount); + + return deletedCount; + } + + /** + * Schedule theSearchPid for deletion assuming it has theNumberOfResults SearchResults attached. + * + * We accumulate a batch of search pids for deletion, and then do a bulk DML as we reach a threshold number + * of SearchResults. + * + * @param theSearchPid pk of the Search + * @param theNumberOfResults the number of SearchResults attached + */ + private void deleteSearchAndResults(long theSearchPid, int theNumberOfResults) { + ourLog.trace("Buffering deletion of search pid {} and {} results", theSearchPid, theNumberOfResults); + + myDeleteSearchBatch.add(theSearchPid); + + if (theNumberOfResults > ourMaximumResultsToDeleteInOneCommit) { + // don't buffer this one - do it inline + deleteSearchResultsByChunk(theSearchPid, theNumberOfResults); + return; + } + myDeleteSearchResultsBatch.add(theSearchPid); + myDeleteSearchResultsBatchCount += theNumberOfResults; + + if (myDeleteSearchResultsBatchCount > ourMaximumResultsToDeleteInOneCommit) { + flushSearchResultDeletes(); + } + + if (myDeleteSearchBatch.size() > ourMaximumResultsToDeleteInOneStatement) { + // flush the results to make sure we don't have any references. + flushSearchResultDeletes(); + + flushSearchAndIncludeDeletes(); + } + } + + /** + * If this Search has more results than our max delete size, + * delete in by itself in range chunks. + * @param theSearchPid the target Search pid + * @param theNumberOfResults the number of search results present + */ + private void deleteSearchResultsByChunk(long theSearchPid, int theNumberOfResults) { + ourLog.debug( + "Search {} is large: has {} results. Deleting results in chunks.", + theSearchPid, + theNumberOfResults); + for (int rangeEnd = theNumberOfResults; rangeEnd >= 0; rangeEnd -= ourMaximumResultsToDeleteInOneCommit) { + int rangeStart = rangeEnd - ourMaximumResultsToDeleteInOneCommit; + ourLog.trace("Deleting results for search {}: {} - {}", theSearchPid, rangeStart, rangeEnd); + mySearchResultDao.deleteBySearchIdInRange(theSearchPid, rangeStart, rangeEnd); + commitOpenChanges(); + } + } + + private void flushSearchAndIncludeDeletes() { + if (myDeleteSearchBatch.isEmpty()) { + return; + } + ourLog.debug("Deleting {} Search records", myDeleteSearchBatch.size()); + // referential integrity requires we delete includes before the search + mySearchIncludeDao.deleteForSearch(myDeleteSearchBatch); + mySearchDao.deleteByPids(myDeleteSearchBatch); + myDeleteSearchBatch.clear(); + commitOpenChanges(); + } + + private void flushSearchResultDeletes() { + if (myDeleteSearchResultsBatch.isEmpty()) { + return; + } + ourLog.debug( + "Deleting {} Search Results from {} searches", + myDeleteSearchResultsBatchCount, + myDeleteSearchResultsBatch.size()); + mySearchResultDao.deleteBySearchIds(myDeleteSearchResultsBatch); + myDeleteSearchResultsBatch.clear(); + myDeleteSearchResultsBatchCount = 0; + commitOpenChanges(); + } + + IExecutionBuilder getTxBuilder() { + return myTransactionService.withSystemRequest().withRequestPartitionId(myRequestPartitionId); + } + + private void run() { + ourLog.debug("Searching for searches which are before {}", myCutoffForDeletion); + + // this tx builder is not really for tx management. + // Instead, it is used bind a Hibernate session + connection to this thread. + // We will run a streaming query to look for work, and then commit changes in batches during the loops. + getTxBuilder().execute(theStatus -> { + try { + markDeletedInBatches(); + + throwIfDeadlineExpired(); + + // Delete searches that are marked as deleted + int deletedCount = deleteMarkedSearchesInBatches(); + + throwIfDeadlineExpired(); + + if ((ourLog.isDebugEnabled() || HapiSystemProperties.isTestModeEnabled()) && (deletedCount > 0)) { + Long total = mySearchDao.count(); + ourLog.debug("Deleted {} searches, {} remaining", deletedCount, total); + } + } catch (DeadlineException theTimeoutException) { + ourLog.warn(theTimeoutException.getMessage()); + } + + return null; + }); + } + + /** + * Stream through a list of pids before our cutoff, and set myDeleted=true in batches in a DML statement. + */ + private void markDeletedInBatches() { + + try (Stream toMarkDeleted = + mySearchDao.findWhereCreatedBefore(myCutoffForDeletion, new Date(now()))) { + assert toMarkDeleted != null; + + toMarkDeleted.forEach(nextSearchToDelete -> { + throwIfDeadlineExpired(); + + if (myUpdateDeletedFlagBatch.size() >= ourMaximumResultsToDeleteInOneStatement) { + flushDeleteMarks(); + } + ourLog.trace("Marking search with PID {} as ready for deletion", nextSearchToDelete); + myUpdateDeletedFlagBatch.add(nextSearchToDelete); + }); + + flushDeleteMarks(); + } + } + } + + /** + * Marker to abandon our delete run when we are over time. + */ + private static class DeadlineException extends RuntimeException { + public DeadlineException(String message) { + super(message); + } + } + @Override - public void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId) { + public void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline) { HapiTransactionService.noTransactionAllowed(); if (!myStorageSettings.isExpireSearchResults()) { return; } + final Date cutoff = getCutoff(); + + final DeleteRun run = new DeleteRun(theDeadline, cutoff, theRequestPartitionId); + + run.run(); + } + + @Nonnull + private Date getCutoff() { long cutoffMillis = myStorageSettings.getExpireSearchResultsAfterMillis(); if (myStorageSettings.getReuseCachedSearchResultsForMillis() != null) { cutoffMillis = cutoffMillis + myStorageSettings.getReuseCachedSearchResultsForMillis(); @@ -189,108 +432,16 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { new InstantType(cutoff), new InstantType(new Date(now()))); } - - ourLog.debug("Searching for searches which are before {}", cutoff); - - // Mark searches as deleted if they should be - final Slice toMarkDeleted = myTransactionService - .withSystemRequestOnPartition(theRequestPartitionId) - .execute(theStatus -> mySearchDao.findWhereCreatedBefore( - cutoff, new Date(), PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))); - assert toMarkDeleted != null; - for (final Long nextSearchToDelete : toMarkDeleted) { - ourLog.debug("Deleting search with PID {}", nextSearchToDelete); - myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> { - mySearchDao.updateDeleted(nextSearchToDelete, true); - return null; - }); - } - - // Delete searches that are marked as deleted - final Slice toDelete = myTransactionService - .withSystemRequestOnPartition(theRequestPartitionId) - .execute(theStatus -> - mySearchDao.findDeleted(PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))); - assert toDelete != null; - for (final Long nextSearchToDelete : toDelete) { - ourLog.debug("Deleting search with PID {}", nextSearchToDelete); - myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> { - deleteSearch(nextSearchToDelete); - return null; - }); - } - - int count = toDelete.getContent().size(); - if (count > 0) { - if (ourLog.isDebugEnabled() || HapiSystemProperties.isTestModeEnabled()) { - Long total = myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> mySearchDao.count()); - ourLog.debug("Deleted {} searches, {} remaining", count, total); - } - } - } - - private void deleteSearch(final Long theSearchPid) { - mySearchDao.findById(theSearchPid).ifPresent(searchToDelete -> { - mySearchIncludeDao.deleteForSearch(searchToDelete.getId()); - - /* - * Note, we're only deleting up to 500 results in an individual search here. This - * is to prevent really long running transactions in cases where there are - * huge searches with tons of results in them. By the time we've gotten here - * we have marked the parent Search entity as deleted, so it's not such a - * huge deal to be only partially deleting search results. They'll get deleted - * eventually - */ - int max = ourMaximumResultsToDeleteInOnePass; - Slice resultPids = mySearchResultDao.findForSearch(PageRequest.of(0, max), searchToDelete.getId()); - if (resultPids.hasContent()) { - List> partitions = - Lists.partition(resultPids.getContent(), ourMaximumResultsToDeleteInOneStatement); - for (List nextPartition : partitions) { - mySearchResultDao.deleteByIds(nextPartition); - } - } - - // Only delete if we don't have results left in this search - if (resultPids.getNumberOfElements() < max) { - ourLog.debug( - "Deleting search {}/{} - Created[{}]", - searchToDelete.getId(), - searchToDelete.getUuid(), - new InstantType(searchToDelete.getCreated())); - mySearchDao.deleteByPid(searchToDelete.getId()); - } else { - ourLog.debug( - "Purged {} search results for deleted search {}/{}", - resultPids.getSize(), - searchToDelete.getId(), - searchToDelete.getUuid()); - } - }); - } - - @VisibleForTesting - public static void setMaximumSearchesToCheckForDeletionCandidacyForUnitTest( - int theMaximumSearchesToCheckForDeletionCandidacy) { - ourMaximumSearchesToCheckForDeletionCandidacy = theMaximumSearchesToCheckForDeletionCandidacy; + return cutoff; } @VisibleForTesting public static void setMaximumResultsToDeleteInOnePassForUnitTest(int theMaximumResultsToDeleteInOnePass) { - ourMaximumResultsToDeleteInOnePass = theMaximumResultsToDeleteInOnePass; + ourMaximumResultsToDeleteInOneCommit = theMaximumResultsToDeleteInOnePass; } @VisibleForTesting - public static void setMaximumResultsToDeleteForUnitTest(int theMaximumResultsToDelete) { + public static void setMaximumResultsToDeleteInOneStatement(int theMaximumResultsToDelete) { ourMaximumResultsToDeleteInOneStatement = theMaximumResultsToDelete; } @@ -302,7 +453,7 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { ourNowForUnitTests = theNowForUnitTests; } - private static long now() { + public static long now() { if (ourNowForUnitTests != null) { return ourNowForUnitTests; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java index 34c662b83f7..8c9ab6f0ec1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.entity.Search; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Optional; public interface ISearchCacheSvc { @@ -86,5 +87,10 @@ public interface ISearchCacheSvc { * if they have some other mechanism for expiring stale results other than manually looking for them * and deleting them. */ - void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId); + void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline); + + @Deprecated(since = "6.10", forRemoval = true) // wipmb delete once cdr merges + default void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId) { + pollForStaleSearchesAndDeleteThem(theRequestPartitionId, Instant.now().plus(1, ChronoUnit.MINUTES)); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java index 86e85a85c9b..893aa6e6744 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; +import java.util.Optional; import static ca.uhn.fhir.jpa.model.entity.PersistedResourceModifiedMessageEntityPK.with; @@ -92,9 +94,43 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif @Override public ResourceModifiedMessage inflatePersistedResourceModifiedMessage( - IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { + ResourceModifiedMessage theResourceModifiedMessage) { - return inflateResourceModifiedMessageFromEntity((ResourceModifiedEntity) thePersistedResourceModifiedMessage); + return inflateResourceModifiedMessageFromEntity(createEntityFrom(theResourceModifiedMessage)); + } + + @Override + public Optional inflatePersistedResourceModifiedMessageOrNull( + ResourceModifiedMessage theResourceModifiedMessage) { + ResourceModifiedMessage inflatedResourceModifiedMessage = null; + + try { + inflatedResourceModifiedMessage = inflatePersistedResourceModifiedMessage(theResourceModifiedMessage); + } catch (ResourceNotFoundException e) { + IdDt idDt = new IdDt( + theResourceModifiedMessage.getPayloadType(myFhirContext), + theResourceModifiedMessage.getPayloadId(), + theResourceModifiedMessage.getPayloadVersion()); + + ourLog.warn("Scheduled submission will be ignored since resource {} cannot be found", idDt.getIdPart(), e); + } catch (Exception ex) { + ourLog.error("Unknown error encountered on inflation of resources.", ex); + } + + return Optional.ofNullable(inflatedResourceModifiedMessage); + } + + @Override + public ResourceModifiedMessage createResourceModifiedMessageFromEntityWithoutInflation( + IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { + ResourceModifiedMessage resourceModifiedMessage = getPayloadLessMessageFromString( + ((ResourceModifiedEntity) thePersistedResourceModifiedMessage).getSummaryResourceModifiedMessage()); + + IdDt resourceId = + createIdDtFromResourceModifiedEntity((ResourceModifiedEntity) thePersistedResourceModifiedMessage); + resourceModifiedMessage.setPayloadId(resourceId); + + return resourceModifiedMessage; } @Override @@ -112,17 +148,13 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif protected ResourceModifiedMessage inflateResourceModifiedMessageFromEntity( ResourceModifiedEntity theResourceModifiedEntity) { - String resourcePid = - theResourceModifiedEntity.getResourceModifiedEntityPK().getResourcePid(); - String resourceVersion = - theResourceModifiedEntity.getResourceModifiedEntityPK().getResourceVersion(); String resourceType = theResourceModifiedEntity.getResourceType(); ResourceModifiedMessage retVal = getPayloadLessMessageFromString(theResourceModifiedEntity.getSummaryResourceModifiedMessage()); SystemRequestDetails systemRequestDetails = new SystemRequestDetails().setRequestPartitionId(retVal.getPartitionId()); - IdDt resourceIdDt = new IdDt(resourceType, resourcePid, resourceVersion); + IdDt resourceIdDt = createIdDtFromResourceModifiedEntity(theResourceModifiedEntity); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); IBaseResource iBaseResource = dao.read(resourceIdDt, systemRequestDetails, true); @@ -164,6 +196,16 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif } } + private IdDt createIdDtFromResourceModifiedEntity(ResourceModifiedEntity theResourceModifiedEntity) { + String resourcePid = + theResourceModifiedEntity.getResourceModifiedEntityPK().getResourcePid(); + String resourceVersion = + theResourceModifiedEntity.getResourceModifiedEntityPK().getResourceVersion(); + String resourceType = theResourceModifiedEntity.getResourceType(); + + return new IdDt(resourceType, resourcePid, resourceVersion); + } + private static class PayloadLessResourceModifiedMessage extends ResourceModifiedMessage { public PayloadLessResourceModifiedMessage(ResourceModifiedMessage theMsg) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index edc52f2e4a0..447e3058c4d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -2979,7 +2979,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { if (resultList.size() > 1) throw new NonUniqueResultException(Msg.code(911) + "More than one CodeSystem is pointed by forcedId: " - + theForcedId + ". Was constraint " + ResourceTable.IDX_RES_FHIR_ID + " removed?"); + + theForcedId + ". Was constraint " + ResourceTable.IDX_RES_TYPE_FHIR_ID + " removed?"); IFhirResourceDao csDao = myDaoRegistry.getResourceDao("CodeSystem"); IBaseResource cs = myJpaStorageResourceParser.toResource(resultList.get(0), false); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java index 2e2528697ce..df28a3f2689 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; @@ -13,18 +12,24 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.packages.loader.PackageResourceParsingSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; +import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Communication; import org.hl7.fhir.r4.model.DocumentReference; import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageGenerator; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -36,6 +41,9 @@ import javax.annotation.Nonnull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -45,12 +53,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class PackageInstallerSvcImplTest { - public static final String PACKAGE_VERSION = "1.0"; public static final String PACKAGE_ID_1 = "package1"; @@ -65,7 +71,13 @@ public class PackageInstallerSvcImplTest { @Mock private IFhirResourceDao myCodeSystemDao; @Mock + private IFhirResourceDao mySearchParameterDao; + @Mock private IValidationSupport myIValidationSupport; + @Mock + private SearchParameterHelper mySearchParameterHelper; + @Mock + private SearchParameterMap mySearchParameterMap; @Spy private FhirContext myCtx = FhirContext.forR4Cached(); @Spy @@ -77,6 +89,15 @@ public class PackageInstallerSvcImplTest { @InjectMocks private PackageInstallerSvcImpl mySvc; + @Captor + private ArgumentCaptor mySearchParameterMapCaptor; + @Captor + private ArgumentCaptor myCodeSystemCaptor; + @Captor + private ArgumentCaptor mySearchParameterCaptor; + @Captor + private ArgumentCaptor myRequestDetailsCaptor; + @Test public void testPackageCompatibility() { mySvc.assertFhirVersionsAreCompatible("R4", "R4B"); @@ -206,19 +227,7 @@ public class PackageInstallerSvcImplTest { cs.setUrl("http://my-code-system"); cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); - NpmPackage pkg = createPackage(cs, PACKAGE_ID_1); - - when(myPackageVersionDao.findByPackageIdAndVersion(any(), any())).thenReturn(Optional.empty()); - when(myPackageCacheManager.installPackage(any())).thenReturn(pkg); - when(myDaoRegistry.getResourceDao(CodeSystem.class)).thenReturn(myCodeSystemDao); - when(myCodeSystemDao.search(any(), any())).thenReturn(new SimpleBundleProvider(existingCs)); - when(myCodeSystemDao.update(any(),any(RequestDetails.class))).thenReturn(new DaoMethodOutcome()); - - PackageInstallationSpec spec = new PackageInstallationSpec(); - spec.setName(PACKAGE_ID_1); - spec.setVersion(PACKAGE_VERSION); - spec.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); - spec.setPackageContents(packageToBytes(pkg)); + PackageInstallationSpec spec = setupResourceInPackage(existingCs, cs, myCodeSystemDao); // Test mySvc.install(spec); @@ -233,34 +242,108 @@ public class PackageInstallerSvcImplTest { assertEquals("existingcs", codeSystem.getIdPart()); } - @Nonnull - private static byte[] packageToBytes(NpmPackage pkg) throws IOException { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - pkg.save(stream); - byte[] bytes = stream.toByteArray(); - return bytes; + public enum InstallType { + CREATE, UPDATE_WITH_EXISTING, UPDATE, UPDATE_OVERRIDE } - @Captor - private ArgumentCaptor mySearchParameterMapCaptor; - @Captor - private ArgumentCaptor myCodeSystemCaptor; + public static List parameters() { + return List.of( + new Object[]{null, null, null, List.of("Patient"), InstallType.CREATE}, + new Object[]{null, null, "us-core-patient-given", List.of("Patient"), InstallType.UPDATE}, + new Object[]{"individual-given", List.of("Patient", "Practitioner"), "us-core-patient-given", List.of("Patient"), InstallType.UPDATE_WITH_EXISTING}, + new Object[]{"patient-given", List.of("Patient"), "us-core-patient-given", List.of("Patient"), InstallType.UPDATE_OVERRIDE} + ); + } + + @ParameterizedTest + @MethodSource("parameters") + public void testCreateOrUpdate_withSearchParameter(String theExistingId, Collection theExistingBase, + String theInstallId, Collection theInstallBase, + InstallType theInstallType) throws IOException { + // Setup + SearchParameter existingSP = null; + if (theExistingId != null) { + existingSP = createSearchParameter(theExistingId, theExistingBase); + } + SearchParameter installSP = createSearchParameter(theInstallId, theInstallBase); + PackageInstallationSpec spec = setupResourceInPackage(existingSP, installSP, mySearchParameterDao); + + // Test + mySvc.install(spec); + + // Verify + if (theInstallType == InstallType.CREATE) { + verify(mySearchParameterDao, times(1)).create(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } else if (theInstallType == InstallType.UPDATE_WITH_EXISTING){ + verify(mySearchParameterDao, times(2)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } else { + verify(mySearchParameterDao, times(1)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } + + Iterator iteratorSP = mySearchParameterCaptor.getAllValues().iterator(); + if (theInstallType == InstallType.UPDATE_WITH_EXISTING) { + SearchParameter capturedSP = iteratorSP.next(); + assertEquals(theExistingId, capturedSP.getIdPart()); + List expectedBase = new ArrayList<>(theExistingBase); + expectedBase.removeAll(theInstallBase); + assertEquals(expectedBase, capturedSP.getBase().stream().map(CodeType::getCode).toList()); + } + SearchParameter capturedSP = iteratorSP.next(); + if (theInstallType == InstallType.UPDATE_OVERRIDE) { + assertEquals(theExistingId, capturedSP.getIdPart()); + } else { + assertEquals(theInstallId, capturedSP.getIdPart()); + } + assertEquals(theInstallBase, capturedSP.getBase().stream().map(CodeType::getCode).toList()); + } + + private PackageInstallationSpec setupResourceInPackage(IBaseResource myExistingResource, IBaseResource myInstallResource, + IFhirResourceDao myFhirResourceDao) throws IOException { + NpmPackage pkg = createPackage(myInstallResource, myInstallResource.getClass().getSimpleName()); + + when(myPackageVersionDao.findByPackageIdAndVersion(any(), any())).thenReturn(Optional.empty()); + when(myPackageCacheManager.installPackage(any())).thenReturn(pkg); + when(myDaoRegistry.getResourceDao(myInstallResource.getClass())).thenReturn(myFhirResourceDao); + when(myFhirResourceDao.search(any(), any())).thenReturn(myExistingResource != null ? + new SimpleBundleProvider(myExistingResource) : new SimpleBundleProvider()); + if (myInstallResource.getClass().getSimpleName().equals("SearchParameter")) { + when(mySearchParameterHelper.buildSearchParameterMapFromCanonical(any())).thenReturn(Optional.of(mySearchParameterMap)); + } + + PackageInstallationSpec spec = new PackageInstallationSpec(); + spec.setName(PACKAGE_ID_1); + spec.setVersion(PACKAGE_VERSION); + spec.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + pkg.save(stream); + spec.setPackageContents(stream.toByteArray()); + + return spec; + } @Nonnull - private NpmPackage createPackage(CodeSystem cs, String packageId) throws IOException { + private NpmPackage createPackage(IBaseResource theResource, String theResourceType) { PackageGenerator manifestGenerator = new PackageGenerator(); - manifestGenerator.name(packageId); + manifestGenerator.name(PACKAGE_ID_1); manifestGenerator.version(PACKAGE_VERSION); manifestGenerator.description("a package"); manifestGenerator.fhirVersions(List.of(FhirVersionEnum.R4.getFhirVersionString())); + String csString = myCtx.newJsonParser().encodeResourceToString(theResource); NpmPackage pkg = NpmPackage.empty(manifestGenerator); - - String csString = myCtx.newJsonParser().encodeResourceToString(cs); - pkg.addFile("package", "cs.json", csString.getBytes(StandardCharsets.UTF_8), "CodeSystem"); + pkg.addFile("package", theResourceType + ".json", csString.getBytes(StandardCharsets.UTF_8), theResourceType); return pkg; } - + private static SearchParameter createSearchParameter(String theId, Collection theBase) { + SearchParameter searchParameter = new SearchParameter(); + if (theId != null) { + searchParameter.setId(new IdType("SearchParameter", theId)); + } + searchParameter.setCode("someCode"); + theBase.forEach(base -> searchParameter.getBase().add(new CodeType(base))); + searchParameter.setExpression("someExpression"); + return searchParameter; + } } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java index 1e619d17e99..22274d942d3 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; @@ -72,9 +73,9 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { public void testLastNChunking() { runInTransaction(() -> { - for (Search search : mySearchDao.findAll()) { - mySearchDao.updateDeleted(search.getId(), true); - } + Set all = mySearchDao.findAll().stream().map(Search::getId).collect(Collectors.toSet()); + + mySearchDao.updateDeleted(all, true); }); // Set up search parameters that will return 75 Observations. 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 f516209a3cd..bf1ca3b56ac 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 @@ -76,7 +76,7 @@ import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.persistence.Version; -import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_FHIR_ID; +import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_TYPE_FHIR_ID; @Indexed(routingBinder = @RoutingBinderRef(type = ResourceTableRoutingBinder.class)) @Entity @@ -84,12 +84,13 @@ import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_FHIR_ID; name = ResourceTable.HFJ_RESOURCE, uniqueConstraints = { @UniqueConstraint( - name = IDX_RES_FHIR_ID, - columnNames = {"FHIR_ID", "RES_TYPE"}) + name = IDX_RES_TYPE_FHIR_ID, + columnNames = {"RES_TYPE", "FHIR_ID"}) }, indexes = { // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE @Index(name = "IDX_RES_DATE", columnList = BaseHasResource.RES_UPDATED), + @Index(name = "IDX_RES_FHIR_ID", columnList = "FHIR_ID"), @Index( name = "IDX_RES_TYPE_DEL_UPDATED", columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"), @@ -100,10 +101,11 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas public static final int RESTYPE_LEN = 40; public static final String HFJ_RESOURCE = "HFJ_RESOURCE"; public static final String RES_TYPE = "RES_TYPE"; + public static final String FHIR_ID = "FHIR_ID"; private static final int MAX_LANGUAGE_LENGTH = 20; private static final long serialVersionUID = 1L; public static final int MAX_FORCED_ID_LENGTH = 100; - public static final String IDX_RES_FHIR_ID = "IDX_RES_FHIR_ID"; + public static final String IDX_RES_TYPE_FHIR_ID = "IDX_RES_TYPE_FHIR_ID"; /** * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB @@ -381,7 +383,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas * Will be null during insert time until the first read. */ @Column( - name = "FHIR_ID", + name = FHIR_ID, // [A-Za-z0-9\-\.]{1,64} - https://www.hl7.org/fhir/datatypes.html#id length = 64, // we never update this after insert, and the Generator will otherwise "dirty" the object. diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java index 40a4658aeb5..0114fba0b7a 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java @@ -51,6 +51,8 @@ public class ResourceMetaParams { Map>> resourceMetaAndParams = new HashMap<>(); resourceMetaParams.put(IAnyResource.SP_RES_ID, StringParam.class); resourceMetaAndParams.put(IAnyResource.SP_RES_ID, StringAndListParam.class); + resourceMetaParams.put(Constants.PARAM_PID, TokenParam.class); + resourceMetaAndParams.put(Constants.PARAM_PID, TokenAndListParam.class); resourceMetaParams.put(Constants.PARAM_TAG, TokenParam.class); resourceMetaAndParams.put(Constants.PARAM_TAG, TokenAndListParam.class); resourceMetaParams.put(Constants.PARAM_PROFILE, UriParam.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java index 2e4dff6e7e8..641e732b642 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java @@ -33,7 +33,9 @@ import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.BundleBuilder; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.text.StringSubstitutor; @@ -48,6 +50,7 @@ import org.springframework.messaging.MessagingException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static ca.uhn.fhir.jpa.subscription.util.SubscriptionUtil.createRequestDetailForPartitionedRequest; @@ -60,6 +63,9 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl @Autowired protected SubscriptionRegistry mySubscriptionRegistry; + @Autowired + protected IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -149,6 +155,13 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl return builder.getBundle(); } + protected Optional inflateResourceModifiedMessageFromDeliveryMessage( + ResourceDeliveryMessage theMsg) { + ResourceModifiedMessage payloadLess = + new ResourceModifiedMessage(theMsg.getPayloadId(myFhirContext), theMsg.getOperationType()); + return myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(payloadLess); + } + @VisibleForTesting public void setFhirContextForUnitTest(FhirContext theCtx) { myFhirContext = theCtx; @@ -174,6 +187,12 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl myMatchUrlService = theMatchUrlService; } + @VisibleForTesting + public void setResourceModifiedMessagePersistenceSvcForUnitTest( + IResourceModifiedMessagePersistenceSvc theResourceModifiedMessagePersistenceSvc) { + myResourceModifiedMessagePersistenceSvc = theResourceModifiedMessagePersistenceSvc; + } + public IInterceptorBroadcaster getInterceptorBroadcaster() { return myInterceptorBroadcaster; } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java index 01ccf19397a..80275a84317 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.EncodingEnum; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.StringUtils; @@ -33,6 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -73,7 +75,7 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv if (isNotBlank(subscription.getPayloadString())) { EncodingEnum encoding = EncodingEnum.forContentType(subscription.getPayloadString()); if (encoding != null) { - payload = theMessage.getPayloadString(); + payload = getPayloadStringFromMessageOrEmptyString(theMessage); } } @@ -112,4 +114,24 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv public IEmailSender getEmailSender() { return myEmailSender; } + + /** + * Get the payload string, fetch it from the DB when the payload is null. + */ + private String getPayloadStringFromMessageOrEmptyString(ResourceDeliveryMessage theMessage) { + String payload = theMessage.getPayloadString(); + + if (theMessage.getPayload(myCtx) != null) { + return payload; + } + + Optional inflatedMessage = + inflateResourceModifiedMessageFromDeliveryMessage(theMessage); + if (inflatedMessage.isEmpty()) { + return ""; + } + + payload = inflatedMessage.get().getPayloadString(); + return payload; + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java index b801d0e8f95..c15854c143e 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java @@ -39,6 +39,7 @@ import org.springframework.messaging.MessagingException; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -66,7 +67,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel IBaseResource payloadResource = createDeliveryBundleForPayloadSearchCriteria( theSubscription, theWrappedMessageToSend.getPayload().getPayload(myFhirContext)); ResourceModifiedJsonMessage newWrappedMessageToSend = - convertDeliveryMessageToResourceModifiedMessage(theSourceMessage, payloadResource); + convertDeliveryMessageToResourceModifiedJsonMessage(theSourceMessage, payloadResource); theWrappedMessageToSend.setPayload(newWrappedMessageToSend.getPayload()); payloadId = payloadResource.getIdElement().toUnqualifiedVersionless().getValue(); @@ -82,7 +83,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel .getValue()); } - private ResourceModifiedJsonMessage convertDeliveryMessageToResourceModifiedMessage( + private ResourceModifiedJsonMessage convertDeliveryMessageToResourceModifiedJsonMessage( ResourceDeliveryMessage theMsg, IBaseResource thePayloadResource) { ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType()); @@ -96,8 +97,17 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException, URISyntaxException { CanonicalSubscription subscription = theMessage.getSubscription(); IBaseResource payloadResource = theMessage.getPayload(myFhirContext); + if (payloadResource == null) { + Optional inflatedMsg = + inflateResourceModifiedMessageFromDeliveryMessage(theMessage); + if (inflatedMsg.isEmpty()) { + return; + } + payloadResource = inflatedMsg.get().getPayload(myFhirContext); + } + ResourceModifiedJsonMessage messageWrapperToSend = - convertDeliveryMessageToResourceModifiedMessage(theMessage, payloadResource); + convertDeliveryMessageToResourceModifiedJsonMessage(theMessage, payloadResource); // Interceptor call: SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY HookParams params = new HookParams() diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java index 55914efdde5..405cd94796d 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.subscription.SubscriptionConstants; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.SubscriptionUtil; import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -41,6 +42,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; +import java.util.Optional; import javax.annotation.Nonnull; /** @@ -64,6 +66,8 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { @Autowired private StorageSettings myStorageSettings; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; /** * Constructor */ @@ -86,6 +90,16 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { switch (payload.getOperationType()) { case CREATE: case UPDATE: + if (payload.getPayload(myFhirContext) == null) { + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull( + payload); + if (inflatedMsg.isEmpty()) { + return; + } + payload = inflatedMsg.get(); + } + activateSubscriptionIfRequired(payload.getNewPayload(myFhirContext)); break; case TRANSACTION: @@ -104,7 +118,7 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { */ public synchronized boolean activateSubscriptionIfRequired(final IBaseResource theSubscription) { // Grab the value for "Subscription.channel.type" so we can see if this - // subscriber applies.. + // subscriber applies. CanonicalSubscriptionChannelType subscriptionChannelType = mySubscriptionCanonicalizer.getChannelType(theSubscription); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java index 488a961c89d..d123f33e98a 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; +import ca.uhn.fhir.jpa.subscription.channel.api.PayloadTooLargeException; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; @@ -156,8 +157,21 @@ public class SubscriptionMatchDeliverer { ourLog.warn("Failed to send message to Delivery Channel."); } } catch (RuntimeException e) { - ourLog.error("Failed to send message to Delivery Channel", e); - throw new RuntimeException(Msg.code(7) + "Failed to send message to Delivery Channel", e); + if (e.getCause() instanceof PayloadTooLargeException) { + ourLog.warn("Failed to send message to Delivery Channel because the payload size is larger than broker " + + "max message size. Retry is about to be performed without payload."); + ResourceDeliveryJsonMessage msgPayloadLess = nullOutPayload(theWrappedMsg); + trySendToDeliveryChannel(msgPayloadLess, theDeliveryChannel); + } else { + ourLog.error("Failed to send message to Delivery Channel", e); + throw new RuntimeException(Msg.code(7) + "Failed to send message to Delivery Channel", e); + } } } + + private ResourceDeliveryJsonMessage nullOutPayload(ResourceDeliveryJsonMessage theWrappedMsg) { + ResourceDeliveryMessage resourceDeliveryMessage = theWrappedMsg.getPayload(); + resourceDeliveryMessage.setPayloadToNull(); + return new ResourceDeliveryJsonMessage(resourceDeliveryMessage); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java index 08623ae0221..f2ef0d1302e 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -40,6 +41,7 @@ import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import java.util.Collection; +import java.util.Optional; import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.server.messaging.BaseResourceMessage.OperationTypeEnum.DELETE; @@ -64,6 +66,9 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { @Autowired private SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + /** * Constructor */ @@ -97,6 +102,16 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { return; } + if (theMsg.getPayload(myFhirContext) == null) { + // inflate the message and ignore any resource that cannot be found. + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(theMsg); + if (inflatedMsg.isEmpty()) { + return; + } + theMsg = inflatedMsg.get(); + } + // Interceptor call: SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED HookParams params = new HookParams().add(ResourceModifiedMessage.class, theMsg); if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED, params)) { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java index 33d655d6a78..33861b5e205 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java @@ -20,9 +20,11 @@ package ca.uhn.fhir.jpa.subscription.submit.interceptor; import ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedProcessingSchedulerSvc; +import ca.uhn.fhir.jpa.subscription.channel.api.PayloadTooLargeException; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -49,11 +51,33 @@ public class SynchronousSubscriptionMatcherInterceptor extends SubscriptionMatch @Override public void afterCommit() { - myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + doSubmitResourceModified(theResourceModifiedMessage); } }); } else { + doSubmitResourceModified(theResourceModifiedMessage); + } + } + + /** + * Submit the message through the broker channel to the matcher. + * + * Note: most of our integrated tests for subscription assume we can successfully inflate the message and therefore + * does not run with an actual database to persist the data. In these cases, submitting the complete message (i.e. + * with payload) is OK. However, there are a few tests that do not assume it and do run with an actual DB. For them, + * we should null out the payload body before submitting. This try-catch block only covers the case where the + * payload is too large, which is enough for now. However, for better practice we might want to consider splitting + * this interceptor into two, each for tests with/without DB connection. + * @param theResourceModifiedMessage + */ + private void doSubmitResourceModified(ResourceModifiedMessage theResourceModifiedMessage) { + try { myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + } catch (MessageDeliveryException e) { + if (e.getCause() instanceof PayloadTooLargeException) { + theResourceModifiedMessage.setPayloadToNull(); + myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + } } } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java index 99d291959ad..e57813bbc1a 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java @@ -35,7 +35,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedConsumerWithRetries; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.r5.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.event.ContextRefreshedEvent; @@ -45,8 +44,6 @@ import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; -import java.util.Optional; - import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber.SUBSCRIPTION_MATCHING_CHANNEL_NAME; /** @@ -151,12 +148,11 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, boolean wasDeleted = deletePersistedResourceModifiedMessage( thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk()); - Optional optionalResourceModifiedMessage = - inflatePersistedResourceMessage(thePersistedResourceModifiedMessage); + // submit the resource modified message with empty payload, actual inflation is done by the matcher. + resourceModifiedMessage = + createResourceModifiedMessageWithoutInflation(thePersistedResourceModifiedMessage); - if (wasDeleted && optionalResourceModifiedMessage.isPresent()) { - // the PK did exist and we were able to deleted it, ie, we are the only one processing the message - resourceModifiedMessage = optionalResourceModifiedMessage.get(); + if (wasDeleted) { submitResourceModified(resourceModifiedMessage); } } catch (MessageDeliveryException exception) { @@ -186,32 +182,10 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, }; } - private Optional inflatePersistedResourceMessage( + private ResourceModifiedMessage createResourceModifiedMessageWithoutInflation( IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { - ResourceModifiedMessage resourceModifiedMessage = null; - - try { - resourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage( - thePersistedResourceModifiedMessage); - - } catch (ResourceNotFoundException e) { - IPersistedResourceModifiedMessagePK persistedResourceModifiedMessagePk = - thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk(); - - IdType idType = new IdType( - thePersistedResourceModifiedMessage.getResourceType(), - persistedResourceModifiedMessagePk.getResourcePid(), - persistedResourceModifiedMessagePk.getResourceVersion()); - - ourLog.warn( - "Scheduled submission will be ignored since resource {} cannot be found", - idType.asStringValue(), - e); - } catch (Exception ex) { - ourLog.error("Unknown error encountered on inflation of resources.", ex); - } - - return Optional.ofNullable(resourceModifiedMessage); + return myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation( + thePersistedResourceModifiedMessage); } private boolean deletePersistedResourceModifiedMessage(IPersistedResourceModifiedMessagePK theResourceModifiedPK) { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java index f8a195ca174..758b2389078 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.topic.filter.InMemoryTopicFilterMatcher; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.Logs; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.SubscriptionTopic; @@ -42,6 +43,7 @@ import org.springframework.messaging.MessagingException; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import javax.annotation.Nonnull; public class SubscriptionTopicMatchingSubscriber implements MessageHandler { @@ -73,6 +75,9 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { @Autowired private InMemoryTopicFilterMatcher myInMemoryTopicFilterMatcher; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + public SubscriptionTopicMatchingSubscriber(FhirContext theFhirContext) { myFhirContext = theFhirContext; } @@ -88,6 +93,16 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload(); + if (msg.getPayload(myFhirContext) == null) { + // inflate the message and ignore any resource that cannot be found. + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(msg); + if (inflatedMsg.isEmpty()) { + return; + } + msg = inflatedMsg.get(); + } + // Interceptor call: SUBSCRIPTION_TOPIC_BEFORE_PERSISTED_RESOURCE_CHECKED HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg); if (!myInterceptorBroadcaster.callHooks( diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java index 8bfb71cb182..16789be59d3 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java @@ -8,10 +8,13 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; +import ca.uhn.fhir.jpa.subscription.match.deliver.email.SubscriptionDeliveringEmailSubscriber; import ca.uhn.fhir.jpa.subscription.match.deliver.message.SubscriptionDeliveringMessageSubscriber; import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; @@ -26,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.fasterxml.jackson.core.JsonProcessingException; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; @@ -33,6 +37,8 @@ import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; @@ -57,6 +63,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -71,6 +78,7 @@ public class BaseSubscriptionDeliverySubscriberTest { private SubscriptionDeliveringRestHookSubscriber mySubscriber; private SubscriptionDeliveringMessageSubscriber myMessageSubscriber; + private SubscriptionDeliveringEmailSubscriber myEmailSubscriber; private final FhirContext myCtx = FhirContext.forR4(); @Mock @@ -96,6 +104,12 @@ public class BaseSubscriptionDeliverySubscriberTest { @Mock private MatchUrlService myMatchUrlService; + @Mock + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + + @Mock + private IEmailSender myEmailSender; + @BeforeEach public void before() { mySubscriber = new SubscriptionDeliveringRestHookSubscriber(); @@ -109,8 +123,15 @@ public class BaseSubscriptionDeliverySubscriberTest { myMessageSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); myMessageSubscriber.setDaoRegistryForUnitTest(myDaoRegistry); myMessageSubscriber.setMatchUrlServiceForUnitTest(myMatchUrlService); + myMessageSubscriber.setResourceModifiedMessagePersistenceSvcForUnitTest(myResourceModifiedMessagePersistenceSvc); myCtx.setRestfulClientFactory(myRestfulClientFactory); when(myRestfulClientFactory.newGenericClient(any())).thenReturn(myGenericClient); + + myEmailSubscriber = new SubscriptionDeliveringEmailSubscriber(myEmailSender); + myEmailSubscriber.setFhirContextForUnitTest(myCtx); + myEmailSubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster); + myEmailSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); + myEmailSubscriber.setResourceModifiedMessagePersistenceSvcForUnitTest(myResourceModifiedMessagePersistenceSvc); } @Test @@ -400,6 +421,38 @@ public class BaseSubscriptionDeliverySubscriberTest { } } + @ParameterizedTest + @ValueSource(strings = {"message", "email"}) + public void testMessageAndEmailSubscriber_whenPayloadIsNull_shouldTryInflateMessage(String theSubscriber) { + // setup + when(myInterceptorBroadcaster.callHooks(any(), any())).thenReturn(true); + + Patient patient = generatePatient(); + + CanonicalSubscription subscription = generateSubscription(); + + ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); + payload.setSubscription(subscription); + payload.setPayload(myCtx, patient, EncodingEnum.JSON); + payload.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE); + + // mock the inflated message + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(any()); + + // this will null out the payload but keep the resource id and version. + payload.setPayloadToNull(); + + // execute & verify + switch (theSubscriber) { + case "message" -> + assertThrows(MessagingException.class, () -> myMessageSubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload))); + case "email" -> + assertThrows(MessagingException.class, () -> myEmailSubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload))); + } + + verify(myResourceModifiedMessagePersistenceSvc, times(1)).inflatePersistedResourceModifiedMessageOrNull(any()); + } + @Nonnull private Patient generatePatient() { Patient patient = new Patient(); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java index f1933548254..817eca140e6 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -90,6 +91,11 @@ public class DaoSubscriptionMatcherTest { public IEmailSender emailSender(){ return mock(IEmailSender.class); } + + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc() { + return mock(IResourceModifiedMessagePersistenceSvc.class); + } } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java index b7fa0f3df6a..1dbb4cf0721 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java @@ -2,8 +2,10 @@ package ca.uhn.fhir.jpa.subscription.module.cache; import ca.uhn.fhir.jpa.subscription.channel.subscription.ISubscriptionDeliveryChannelNamer; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.hl7.fhir.dstu3.model.Subscription; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -18,6 +20,9 @@ public class SubscriptionRegistrySharedTest extends BaseSubscriptionRegistryTest private static final String OTHER_ID = "OTHER_ID"; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + @Configuration public static class SpringConfig { diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java index b53918743a8..76fd776b5e7 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.Subscription; import org.slf4j.Logger; @@ -62,4 +63,9 @@ public class TestSubscriptionDstu3Config { return mock; } + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc() { + return mock(IResourceModifiedMessagePersistenceSvc.class); + } + } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java index e5fa3a5ee8e..b081bbdb37f 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.test.concurrency.IPointcutLatch; import ca.uhn.test.concurrency.PointcutLatch; @@ -54,6 +55,10 @@ import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends BaseSubscriptionDstu3Test { public static final ChannelConsumerSettings CONSUMER_OPTIONS = new ChannelConsumerSettings().setConcurrentConsumers(1); @@ -100,6 +105,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base IInterceptorService myInterceptorRegistry; @Autowired private ISubscriptionDeliveryChannelNamer mySubscriptionDeliveryChannelNamer; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; @BeforeEach public void beforeReset() { @@ -140,6 +147,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base public T sendResource(T theResource, RequestPartitionId theRequestPartitionId) throws InterruptedException { ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE, null, theRequestPartitionId); ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(msg); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.of(msg)); + mySubscriptionMatchingPost.setExpectedCount(1); ourSubscribableChannel.send(message); mySubscriptionMatchingPost.awaitExpected(); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java index 6f7710b042f..7730df2e400 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java @@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscriba import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.BooleanType; @@ -33,6 +34,7 @@ import org.mockito.Mockito; import java.util.Collections; import java.util.List; +import java.util.Optional; import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser.TypeEnum.STARTYPE_EXPRESSION; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -434,6 +436,8 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri SubscriptionCriteriaParser.SubscriptionCriteria mySubscriptionCriteria; @Mock SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; + @Mock + IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; @InjectMocks SubscriptionMatchingSubscriber subscriber; @@ -445,6 +449,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myInterceptorBroadcaster.callHooks( eq(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED), any(HookParams.class))).thenReturn(true); when(mySubscriptionRegistry.getAll()).thenReturn(Collections.emptyList()); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -465,6 +470,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myActiveSubscription.getCriteria()).thenReturn(mySubscriptionCriteria); when(myActiveSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -486,6 +492,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myNonDeleteSubscription.getCriteria()).thenReturn(mySubscriptionCriteria); when(myNonDeleteSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -505,6 +512,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myActiveSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); when(myCanonicalSubscription.getSendDeleteMessages()).thenReturn(true); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java index a14e8794964..4bf3e1fcf3b 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -146,5 +147,10 @@ public class WebsocketConnectionValidatorTest { return mock(IEmailSender.class); } + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc(){ + return mock(IResourceModifiedMessagePersistenceSvc.class); + } + } } 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 7037f682ec5..6fbd1c642c9 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 @@ -2189,21 +2189,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { pm.setSort(new SortSpec(BaseResource.SP_RES_ID)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(id4, id3, id2, id1, idMethodName)); + assertThat(actual, contains(idMethodName, id4, id3, id2, id1)); } @Test diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index 7c2d95ac116..db98eb767bd 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -3323,7 +3323,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.ASC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2)); + assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB")); } 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 184a18fb8aa..7b2b699d85f 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 @@ -2788,21 +2788,21 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { pm.setSort(new SortSpec(IAnyResource.SP_RES_ID)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(id4, id3, id2, id1, idMethodName)); + assertThat(actual.toString(), actual, contains(idMethodName, id4, id3, id2, id1)); } @Test 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 2700b1b2489..e91e3421bf2 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 @@ -138,7 +138,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_FHIR_ID).executeUpdate(); + myEntityManager.createNativeQuery("alter table " + ResourceTable.HFJ_RESOURCE + " drop constraint " + ResourceTable.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/FhirResourceDaoR4QuerySandbox.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java new file mode 100644 index 00000000000..7efc920ccaf --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java @@ -0,0 +1,150 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.jpa.dao.TestDaoSearch; +import ca.uhn.fhir.jpa.test.BaseJpaTest; +import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; +import ca.uhn.fhir.jpa.test.config.TestR4Config; +import ca.uhn.fhir.jpa.util.SqlQuery; +import ca.uhn.fhir.jpa.util.SqlQueryList; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.storage.test.DaoTestDataBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.transaction.PlatformTransactionManager; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; + +/** + * Sandbox for implementing queries. + * This will NOT run during the build - use this class as a convenient + * place to explore, debug, profile, and optimize. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + TestR4Config.class, + TestHSearchAddInConfig.NoFT.class, + DaoTestDataBuilder.Config.class, + TestDaoSearch.Config.class +}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@TestExecutionListeners(listeners = { + DependencyInjectionTestExecutionListener.class + , FhirResourceDaoR4QuerySandbox.TestDirtiesContextTestExecutionListener.class +}) +public class FhirResourceDaoR4QuerySandbox extends BaseJpaTest { + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4QuerySandbox.class); + + @Autowired + PlatformTransactionManager myTxManager; + @Autowired + FhirContext myFhirCtx; + @RegisterExtension + @Autowired + DaoTestDataBuilder myDataBuilder; + @Autowired + TestDaoSearch myTestDaoSearch; + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + @Override + protected FhirContext getFhirContext() { + return myFhirCtx; + } + + List myCapturedQueries = new ArrayList<>(); + @BeforeEach + void registerLoggingInterceptor() { + registerInterceptor(new Object(){ + @Hook(Pointcut.JPA_PERFTRACE_RAW_SQL) + public void captureSql(RequestDetails theRequestDetails, SqlQueryList theQueries) { + for (SqlQuery next : theQueries) { + String output = next.getSql(true, true, true); + ourLog.info("Query: {}", output); + myCapturedQueries.add(output); + } + } + }); + + } + + @Test + public void testSearches_logQueries() { + myDataBuilder.createPatient(); + + myTestDaoSearch.searchForIds("Patient?name=smith"); + + assertThat(myCapturedQueries, not(empty())); + } + + @Test + void testQueryByPid() { + + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?_pid=" + id, id); + } + + @Test + void testQueryByPid_withOtherSPAvoidsResourceTable() { + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?name=smith&_pid=" + id, id); + } + + @Test + void testSortByPid() { + + String id1 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithy")).getIdPart(); + String id2 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithwick")).getIdPart(); + String id3 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsInOrder("sort by server assigned id", "Patient?family=smith&_sort=_pid", id1,id2,id3); + myTestDaoSearch.assertSearchFindsInOrder("reverse sort by server assigned id", "Patient?family=smith&_sort=-_pid", id3,id2,id1); + } + + public static final class TestDirtiesContextTestExecutionListener extends DirtiesContextTestExecutionListener { + + @Override + protected void beforeOrAfterTestClass(TestContext testContext, DirtiesContext.ClassMode requiredClassMode) throws Exception { + if (!testContext.getTestClass().getName().contains("$")) { + super.beforeOrAfterTestClass(testContext, requiredClassMode); + } + } + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index f28fec1fb98..779cd067177 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -176,6 +176,7 @@ import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL; import static ca.uhn.fhir.test.utilities.CustomMatchersUtil.assertDoesNotContainAnyOf; +import static ca.uhn.fhir.util.DateUtils.convertDateToIso8601String; import static org.apache.commons.lang3.StringUtils.countMatches; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.hamcrest.CoreMatchers.is; @@ -447,6 +448,57 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertEquals(0, ids.size()); } + @Test + public void testHasEncounterAndLastUpdated() { + // setup + Patient patientA = new Patient(); + String patientIdA = myPatientDao.create(patientA).getId().toUnqualifiedVersionless().getValue(); + + Patient patientB = new Patient(); + String patientIdB = myPatientDao.create(patientA).getId().toUnqualifiedVersionless().getValue(); + + Encounter encounterA = new Encounter(); + encounterA.getClass_().setSystem("http://snomed.info/sct").setCode("55822004"); + encounterA.getSubject().setReference(patientIdA); + + // record time between encounter A and B + TestUtil.sleepOneClick(); + Date beforeA = new Date(); + TestUtil.sleepOneClick(); + + myEncounterDao.create(encounterA); + + Encounter encounterB = new Encounter(); + encounterB.getClass_().setSystem("http://snomed.info/sct").setCode("55822005"); + encounterB.getSubject().setReference(patientIdB); + + // record time between encounter A and B + TestUtil.sleepOneClick(); + Date beforeB = new Date(); + TestUtil.sleepOneClick(); + + myEncounterDao.create(encounterB); + + // execute + String criteriaA = "_has:Encounter:patient:_lastUpdated=ge" + convertDateToIso8601String(beforeA); + SearchParameterMap mapA = myMatchUrlService.translateMatchUrl(criteriaA, myFhirContext.getResourceDefinition(Patient.class)); + mapA.setLoadSynchronous(true); + myCaptureQueriesListener.clear(); + IBundleProvider resultA = myPatientDao.search(mapA); + myCaptureQueriesListener.logSelectQueries(); + List idsBeforeA = toUnqualifiedVersionlessIdValues(resultA); + + String criteriaB = "_has:Encounter:patient:_lastUpdated=ge" + convertDateToIso8601String(beforeB); + SearchParameterMap mapB = myMatchUrlService.translateMatchUrl(criteriaB, myFhirContext.getResourceDefinition(Patient.class)); + mapB.setLoadSynchronous(true); + IBundleProvider resultB = myPatientDao.search(mapB); + List idsBeforeB = toUnqualifiedVersionlessIdValues(resultB); + + // verify + assertEquals(2, idsBeforeA.size()); + assertEquals(1, idsBeforeB.size()); + } + @Test public void testGenderBirthdateHasCondition() { Patient patient = new Patient(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java index ea781b9e4cc..23ab501a227 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java @@ -89,12 +89,12 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test { map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.ASC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2)); + assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB")); map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.DESC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains(id2, id1, "Patient/AB", "Patient/AA")); + assertThat(ids, contains("Patient/AB", "Patient/AA", id2, id1)); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java index 3236f6b60fd..3d23277dc19 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java @@ -4,15 +4,17 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.TestDaoSearch; -import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases; -import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases; import ca.uhn.fhir.jpa.search.BaseSourceSearchParameterTestCases; +import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases; +import ca.uhn.fhir.jpa.search.IIdSearchTestTemplate; +import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.storage.test.BaseDateSearchDaoTests; import ca.uhn.fhir.storage.test.DaoTestDataBuilder; +import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.test.utilities.ITestDataBuilder.ICreationArgument; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Observation; @@ -36,9 +38,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.not; +/** + * Verify that our query behaviour matches the spec. + * Note: we do not extend BaseJpaR4Test here. + * That does a full purge in @AfterEach which is a bit slow. + * Instead, this test tracks all created resources in DaoTestDataBuilder, and deletes them in teardown. + */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { TestR4Config.class, @@ -256,10 +263,10 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idExM = withObservation(myDataBuilder.withObservationCode("http://example.org", "MValue")).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code"); - assertThat(allIds, hasItems(idAlphaA, idAlphaM, idAlphaZ, idExA, idExD, idExM)); + assertThat(allIds, contains(idAlphaA, idAlphaM, idAlphaZ, idExA, idExD, idExM)); allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code&code=http://example.org|"); - assertThat(allIds, hasItems(idExA, idExD, idExM)); + assertThat(allIds, contains(idExA, idExD, idExM)); } } } @@ -368,7 +375,7 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idAlpha5 = withRiskAssessmentWithProbabilty(0.5).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/RiskAssessment?_sort=probability"); - assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7)); + assertThat(allIds, contains(idAlpha2, idAlpha5, idAlpha7)); } } @@ -491,12 +498,51 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idAlpha5 = withObservationWithValueQuantity(0.5).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/Observation?_sort=value-quantity"); - assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7)); + assertThat(allIds, contains(idAlpha2, idAlpha5, idAlpha7)); } } } + @Test + void testQueryByPid() { + + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?_pid=" + id, id); + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?family=smith&_pid=" + id, id); + } + + @Test + void testSortByPid() { + + String id1 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithy")).getIdPart(); + String id2 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithwick")).getIdPart(); + String id3 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsInOrder("sort by server assigned id", "Patient?family=smith&_sort=_pid", id1,id2,id3); + myTestDaoSearch.assertSearchFindsInOrder("reverse sort by server assigned id", "Patient?family=smith&_sort=-_pid", id3,id2,id1); + } + + @Nested + public class IdSearch implements IIdSearchTestTemplate { + @Override + public TestDaoSearch getSearch() { + return myTestDaoSearch; + } + + @Override + public ITestDataBuilder getBuilder() { + return myDataBuilder; + } + } + // todo mb re-enable this. Some of these fail! @Disabled @Nested diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index d06cba7de2b..ab11fcd27f1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -3352,63 +3352,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { } - @Test - public void testSortById() { - String methodName = "testSortBTyId"; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.setId(methodName + "1"); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType idMethodName1 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless(); - assertEquals(methodName + "1", idMethodName1.getIdPart()); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.setId(methodName + "2"); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType idMethodName2 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless(); - assertEquals(methodName + "2", idMethodName2.getIdPart()); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap pm; - List actual; - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4)); - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4)); - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(id4, id3, id2, id1, idMethodName2, idMethodName1)); - } - @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSortByMissingAttribute(boolean theIndexMissingData) { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java index 18657d1d5b0..26ff93cc64f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.entity.Search; @@ -16,6 +15,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.UUID; @@ -31,22 +32,20 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test { @Autowired private ISearchResultDao mySearchResultDao; - @Autowired - private ISearchCoordinatorSvc mySearchCoordinator; - @Autowired private ISearchCacheSvc myDatabaseCacheSvc; @AfterEach public void after() { DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS); - DatabaseSearchCacheSvcImpl.setMaximumSearchesToCheckForDeletionCandidacyForUnitTest(DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND); } + /** + * Semi-obsolete test. This used to test incremental deletion, but we now work until done or a timeout. + */ @Test public void testDeleteDontMarkPreviouslyMarkedSearchesAsDeleted() { DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(5); - DatabaseSearchCacheSvcImpl.setMaximumSearchesToCheckForDeletionCandidacyForUnitTest(10); runInTransaction(()->{ mySearchResultDao.deleteAll(); @@ -86,28 +85,12 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test { assertEquals(30, mySearchResultDao.count()); }); - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); + myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions(), Instant.now().plus(10, ChronoUnit.SECONDS)); runInTransaction(()->{ // We should delete up to 10, but 3 don't get deleted since they have too many results to delete in one pass - assertEquals(13, mySearchDao.count()); - assertEquals(3, mySearchDao.countDeleted()); - // We delete a max of 5 results per search, so half are gone - assertEquals(15, mySearchResultDao.count()); - }); - - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); - runInTransaction(()->{ - // Once again we attempt to delete 10, but the first 3 don't get deleted and still remain - // (total is 6 because 3 weren't deleted, and they blocked another 3 that might have been) - assertEquals(6, mySearchDao.count()); - assertEquals(6, mySearchDao.countDeleted()); - assertEquals(0, mySearchResultDao.count()); - }); - - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); - runInTransaction(()->{ assertEquals(0, mySearchDao.count()); assertEquals(0, mySearchDao.countDeleted()); + // We delete a max of 5 results per search, so half are gone assertEquals(0, mySearchResultDao.count()); }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java index da9b8ee62e0..25d450771d7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java @@ -22,7 +22,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -53,7 +52,7 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { final NamingSystem namingSystem = new NamingSystem(); namingSystem.getUniqueId().add(new NamingSystem.NamingSystemUniqueIdComponent().setValue("123")); - create(namingSystem); + install(namingSystem); assertEquals(1, myNamingSystemDao.search(SearchParameterMap.newSynchronous(), REQUEST_DETAILS).getAllResources().size()); } @@ -184,7 +183,7 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { } private void createValueSetAndCallCreate(String theOid, String theResourceVersion, String theValueSetVersion, String theUrl, String theCopyright) throws IOException { - create(createValueSet(theOid, theResourceVersion, theValueSetVersion, theUrl, theCopyright)); + install(createValueSet(theOid, theResourceVersion, theValueSetVersion, theUrl, theCopyright)); } @Nonnull @@ -199,8 +198,8 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { return valueSetFromFirstIg; } - private void create(IBaseResource theResource) throws IOException { - mySvc.create(theResource, createInstallationSpec(packageToBytes()), new PackageInstallOutcomeJson()); + private void install(IBaseResource theResource) throws IOException { + mySvc.install(theResource, createInstallationSpec(packageToBytes()), new PackageInstallOutcomeJson()); } @Nonnull diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java index b1c88f6f508..aff55ed8ed3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java @@ -38,7 +38,7 @@ public class PackageInstallerSvcImplRewriteHistoryTest extends BaseJpaR4Test { // execute // red-green this threw a NPE before the fix - mySvc.updateResource(myConceptMapDao, conceptMap); + mySvc.createOrUpdateResource(myConceptMapDao, conceptMap, null); // verify ConceptMap readConceptMap = myConceptMapDao.read(CONCEPT_MAP_TEST_ID); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java index 91d4a521037..273b382d924 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.api.IClientInterceptor; @@ -18,13 +19,16 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Binary; +import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DocumentReference; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +36,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +65,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { public static final byte[] FEW_BYTES = {4, 3, 2, 1}; public static final byte[] SOME_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 8, 9, 0, 10, 9}; public static final byte[] SOME_BYTES_2 = {6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 5, 5, 5, 6}; + public static final byte[] SOME_BYTES_3 = {5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8}; private static final Logger ourLog = LoggerFactory.getLogger(BinaryStorageInterceptorR4Test.class); @Autowired @@ -381,12 +385,8 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { // Create a resource with a big enough docRef DocumentReference docRef = new DocumentReference(); - DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); - content.getAttachment().setContentType("application/octet-stream"); - content.getAttachment().setData(SOME_BYTES); - DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); - content2.getAttachment().setContentType("application/octet-stream"); - content2.getAttachment().setData(SOME_BYTES_2); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); DaoMethodOutcome outcome = myDocumentReferenceDao.create(docRef, mySrd); // Make sure it was externalized @@ -422,18 +422,73 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { } + @Test + public void testCreateBinaryAttachments_bundleWithMultipleDocumentReferences_createdAndReadBackSuccessfully() { + // Create Patient + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().addGiven("Johnny").setFamily("Walker"); + + // Create first DocumentReference with a big enough attachments + DocumentReference docRef = new DocumentReference(); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); + + // Create second DocumentReference with a big enough attachment + DocumentReference docRef2 = new DocumentReference(); + addDocumentAttachmentData(docRef2, SOME_BYTES_3); + + // Create Bundle + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + // Patient entry component + addBundleEntry(bundle, patient, "Patient"); + // First DocumentReference entry component + addBundleEntry(bundle, docRef, "DocumentReference"); + // Second DocumentReference entry component + addBundleEntry(bundle, docRef2, "DocumentReference"); + + // Execute transaction + Bundle output = myClient.transaction().withBundle(bundle).execute(); + ourLog.debug(myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); + + // Verify bundle response + assertEquals(3, output.getEntry().size()); + output.getEntry().forEach(entry -> assertEquals("201 Created", entry.getResponse().getStatus())); + + // Read back and verify first DocumentReference and attachments + IIdType firstDocRef = new IdType(output.getEntry().get(1).getResponse().getLocation()); + DocumentReference firstDoc = myDocumentReferenceDao.read(firstDocRef, mySrd); + assertEquals("application/octet-stream", firstDoc.getContentFirstRep().getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES, firstDoc.getContentFirstRep().getAttachment().getData()); + assertEquals("application/octet-stream", firstDoc.getContent().get(1).getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES_2, firstDoc.getContent().get(1).getAttachment().getData()); + + // Read back and verify second DocumentReference and attachment + IIdType secondDocRef = new IdType(output.getEntry().get(2).getResponse().getLocation()); + DocumentReference secondDoc = myDocumentReferenceDao.read(secondDocRef, mySrd); + assertEquals("application/octet-stream", secondDoc.getContentFirstRep().getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES_3, secondDoc.getContentFirstRep().getAttachment().getData()); + } + + private void addBundleEntry(Bundle theBundle, Resource theResource, String theUrl) { + Bundle.BundleEntryComponent getComponent = new Bundle.BundleEntryComponent(); + Bundle.BundleEntryRequestComponent requestComponent = new Bundle.BundleEntryRequestComponent(); + requestComponent.setMethod(Bundle.HTTPVerb.POST); + requestComponent.setUrl(theUrl); + getComponent.setRequest(requestComponent); + getComponent.setResource(theResource); + getComponent.setFullUrl(IdDt.newRandomUuid().getValue()); + theBundle.addEntry(getComponent); + } @Test public void testUpdateRejectsIncorrectBinary() { // Create a resource with a big enough docRef DocumentReference docRef = new DocumentReference(); - DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); - content.getAttachment().setContentType("application/octet-stream"); - content.getAttachment().setData(SOME_BYTES); - DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); - content2.getAttachment().setContentType("application/octet-stream"); - content2.getAttachment().setData(SOME_BYTES_2); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); DaoMethodOutcome outcome = myDocumentReferenceDao.create(docRef, mySrd); // Make sure it was externalized @@ -449,13 +504,13 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { docRef = new DocumentReference(); docRef.setId(id.toUnqualifiedVersionless()); docRef.setStatus(Enumerations.DocumentReferenceStatus.CURRENT); - content = docRef.addContent(); + DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); content.getAttachment().setContentType("application/octet-stream"); content.getAttachment().getDataElement().addExtension( HapiExtensions.EXT_EXTERNALIZED_BINARY_ID, new StringType(binaryId) ); - content2 = docRef.addContent(); + DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); content2.getAttachment().setContentType("application/octet-stream"); content2.getAttachment().getDataElement().addExtension( HapiExtensions.EXT_EXTERNALIZED_BINARY_ID, @@ -497,5 +552,10 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { } + private void addDocumentAttachmentData(DocumentReference theDocumentReference, byte[] theData) { + DocumentReference.DocumentReferenceContentComponent content = theDocumentReference.addContent(); + content.getAttachment().setContentType("application/octet-stream"); + content.getAttachment().setData(theData); + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java index 6fd56b30ee4..52e288f4727 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; @@ -66,12 +67,15 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.awaitility.Awaitility.await; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.blankOrNullString; import static org.hamcrest.Matchers.hasSize; @@ -189,8 +193,64 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider myServer.getRestfulServer().getInterceptorService().registerInterceptor(myConsentInterceptor); // Perform a search and only allow even + String context = "active consent - hide odd"; consentService.setTarget(new ConsentSvcCantSeeOddNumbered()); - Bundle result = myClient + List returnedIdValues = searchForObservations(); + assertEquals(myObservationIdsEvenOnly.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and only allow odd + context = "active consent - hide even"; + consentService.setTarget(new ConsentSvcCantSeeEvenNumbered()); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIdsOddOnly.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and allow all with a PROCEED + context = "active consent - PROCEED on cache"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and allow all with an AUTHORIZED (no further checking) + context = "active consent - AUTHORIZED after a PROCEED"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + + // Perform a second search and allow all with an AUTHORIZED (no further checking) + // which means we should finally get one from the cache + context = "active consent - AUTHORIZED after AUTHORIZED"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsFromCache(context, capture.getLastResponse()); + + // Perform another search, now with an active consent interceptor that promises not to use canSeeResource. + // Should re-use cache result + context = "active consent - canSeeResource disabled, after AUTHORIZED - should reuse cache"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED, false)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsFromCache(context, capture.getLastResponse()); + + myClient.unregisterInterceptor(capture); + } + + private static void assertResponseIsNotFromCache(String theContext, IHttpResponse lastResponse) { + List cacheOutcome= lastResponse.getHeaders(Constants.HEADER_X_CACHE); + assertThat(theContext + " - No cache response headers", cacheOutcome, empty()); + } + + private static void assertResponseIsFromCache(String theContext, IHttpResponse lastResponse) { + List cacheOutcome = lastResponse.getHeaders(Constants.HEADER_X_CACHE); + assertThat(theContext + " - Response came from cache", cacheOutcome, hasItem(matchesPattern("^HIT from .*"))); + } + + private List searchForObservations() { + Bundle result; + result = myClient .search() .forResource("Observation") .sort() @@ -199,77 +259,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider .count(15) .execute(); List resources = BundleUtil.toListOfResources(myFhirContext, result); - List returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIdsEvenOnly.subList(0, 15), returnedIdValues); - List cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and only allow odd - consentService.setTarget(new ConsentSvcCantSeeEvenNumbered()); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIdsOddOnly.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and allow all with a PROCEED - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and allow all with an AUTHORIZED (no further checking) - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a second search and allow all with an AUTHORIZED (no further checking) - // which means we should finally get one from the cache - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertThat(cacheOutcome.get(0), matchesPattern("^HIT from .*")); - - myClient.unregisterInterceptor(capture); + return toUnqualifiedVersionlessIdValues(resources); } @Test @@ -528,6 +518,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider IConsentService svc = mock(IConsentService.class); when(svc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(svc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); when(svc.canSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.REJECT); consentService.setTarget(svc); @@ -560,6 +551,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider IConsentService svc = mock(IConsentService.class); when(svc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(svc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); when(svc.canSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t -> { IBaseResource resource = t.getArgument(1, IBaseResource.class); if (resource instanceof Organization) { @@ -998,16 +990,27 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider private static class ConsentSvcNop implements IConsentService { private final ConsentOperationStatusEnum myOperationStatus; + private boolean myEnableCanSeeResource = true; private ConsentSvcNop(ConsentOperationStatusEnum theOperationStatus) { myOperationStatus = theOperationStatus; } + private ConsentSvcNop(ConsentOperationStatusEnum theOperationStatus, boolean theEnableCanSeeResource) { + myOperationStatus = theOperationStatus; + myEnableCanSeeResource = theEnableCanSeeResource; + } + @Override public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { return new ConsentOutcome(myOperationStatus); } + @Override + public boolean shouldProcessCanSeeResource(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + return myEnableCanSeeResource; + } + @Override public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { return new ConsentOutcome(ConsentOperationStatusEnum.PROCEED); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 5c5e356a283..139f4df1e5a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.ITestDataBuilder; @@ -40,11 +41,14 @@ import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -65,6 +69,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -424,6 +429,55 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te } + @Test + public void testTransactionPut_withSearchNarrowingInterceptor_createsPatient() { + // setup + IBaseResource patientA = buildPatient(withTenant(TENANT_B), withActiveTrue(), withId("1234a"), + withFamily("Family"), withGiven("Given")); + + Bundle transactioBundle = new Bundle(); + transactioBundle.setType(Bundle.BundleType.TRANSACTION); + transactioBundle.addEntry() + .setFullUrl("http://localhost:8000/TENANT-A/Patient/1234a") + .setResource((Resource) patientA) + .getRequest().setUrl("Patient/1234a").setMethod(Bundle.HTTPVerb.PUT); + + myServer.registerInterceptor(new SearchNarrowingInterceptor()); + + // execute + myClient.transaction().withBundle(transactioBundle).execute(); + + // verify - read back using DAO + SystemRequestDetails requestDetails = new SystemRequestDetails(); + requestDetails.setTenantId(TENANT_B); + Patient patient1 = myPatientDao.read(new IdType("Patient/1234a"), requestDetails); + assertEquals("Family", patient1.getName().get(0).getFamily()); + } + + @ParameterizedTest + @ValueSource(strings = {"Patient/1234a", "TENANT-B/Patient/1234a"}) + public void testTransactionGet_withSearchNarrowingInterceptor_retrievesPatient(String theEntryUrl) { + // setup + createPatient(withTenant(TENANT_B), withActiveTrue(), withId("1234a"), + withFamily("Family"), withGiven("Given")); + + Bundle transactioBundle = new Bundle(); + transactioBundle.setType(Bundle.BundleType.TRANSACTION); + transactioBundle.addEntry() + .getRequest().setUrl(theEntryUrl).setMethod(Bundle.HTTPVerb.GET); + + myServer.registerInterceptor(new SearchNarrowingInterceptor()); + + // execute + Bundle result = myClient.transaction().withBundle(transactioBundle).execute(); + + // verify + assertEquals(1, result.getEntry().size()); + Patient retrievedPatient = (Patient) result.getEntry().get(0).getResource(); + assertNotNull(retrievedPatient); + assertEquals("Family", retrievedPatient.getName().get(0).getFamily()); + } + @Test public void testDirectDaoAccess_PartitionInRequestDetails_Create() { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java index 3d9f3e0a6e9..1e6432f0af1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java @@ -48,7 +48,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { super.after(); DatabaseSearchCacheSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(mySearchCacheSvc); staleSearchDeletingSvc.setCutoffSlackForUnitTest(DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS); - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS); } @@ -108,7 +108,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDeleteVeryLargeSearch() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(10); runInTransaction(() -> { @@ -120,24 +120,21 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setResourceType("Patient"); search = mySearchEntityDao.save(search); - for (int i = 0; i < 15; i++) { - ResourceTable resource = new ResourceTable(); - resource.setPublished(new Date()); - resource.setUpdated(new Date()); - resource.setResourceType("Patient"); - resource = myResourceTableDao.saveAndFlush(resource); + ResourceTable resource = new ResourceTable(); + resource.setPublished(new Date()); + resource.setUpdated(new Date()); + resource.setResourceType("Patient"); + resource = myResourceTableDao.saveAndFlush(resource); + for (int i = 0; i < 50; i++) { SearchResult sr = new SearchResult(search); sr.setOrder(i); sr.setResourcePid(resource.getId()); mySearchResultDao.save(sr); } - }); - // It should take two passes to delete the search fully - runInTransaction(() -> assertEquals(1, mySearchEntityDao.count())); - myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); + // we are able to delete this in one pass. runInTransaction(() -> assertEquals(1, mySearchEntityDao.count())); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); runInTransaction(() -> assertEquals(0, mySearchEntityDao.count())); @@ -146,9 +143,9 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDeleteVerySmallSearch() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); - runInTransaction(() -> { + runInTransaction(() -> { Search search = new Search(); search.setStatus(SearchStatusEnum.FINISHED); search.setUuid(UUID.randomUUID().toString()); @@ -172,9 +169,9 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDontDeleteSearchBeforeExpiry() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); - runInTransaction(() -> { + runInTransaction(() -> { Search search = new Search(); // Expires in one second, so it should not be deleted right away, @@ -186,7 +183,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setCreated(DateUtils.addDays(new Date(), -10000)); search.setSearchType(SearchTypeEnum.SEARCH); search.setResourceType("Patient"); - search = mySearchEntityDao.save(search); + mySearchEntityDao.save(search); }); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java index 2fc3ca8bb20..03b12841055 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java @@ -253,26 +253,28 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test { } @Test - public void testPersistedResourceModifiedMessage_whenFetchFromDb_willEqualOriginalMessage() throws JsonProcessingException { + public void testMethodInflatePersistedResourceModifiedMessage_whenGivenResourceModifiedMessageWithEmptyPayload_willEqualOriginalMessage() { mySubscriptionTestUtil.unregisterSubscriptionInterceptor(); - // given + // setup TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager); Observation obs = sendObservation("zoop", "SNOMED-CT", "theExplicitSource", "theRequestId"); ResourceModifiedMessage originalResourceModifiedMessage = createResourceModifiedMessage(obs); + ResourceModifiedMessage resourceModifiedMessageWithEmptyPayload = createResourceModifiedMessage(obs); + resourceModifiedMessageWithEmptyPayload.setPayloadToNull(); transactionTemplate.execute(tx -> { - IPersistedResourceModifiedMessage persistedResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.persist(originalResourceModifiedMessage); + myResourceModifiedMessagePersistenceSvc.persist(originalResourceModifiedMessage); - // when - ResourceModifiedMessage restoredResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(persistedResourceModifiedMessage); + // execute + ResourceModifiedMessage restoredResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(resourceModifiedMessageWithEmptyPayload); - // then - assertEquals(toJson(originalResourceModifiedMessage), toJson(restoredResourceModifiedMessage)); - assertEquals(originalResourceModifiedMessage, restoredResourceModifiedMessage); + // verify + assertEquals(toJson(originalResourceModifiedMessage), toJson(restoredResourceModifiedMessage)); + assertEquals(originalResourceModifiedMessage, restoredResourceModifiedMessage); - return null; + return null; }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java index 21d1f97c686..f8194204aaa 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java @@ -105,7 +105,7 @@ public class ResourceModifiedSubmitterSvcTest { // given // a successful deletion implies that the message did exist. when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // when boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity()); @@ -134,7 +134,7 @@ public class ResourceModifiedSubmitterSvcTest { // when when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) .thenThrow(new RuntimeException(deleteExMsg)); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())) + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())) .thenThrow(new RuntimeException(inflationExMsg)); // test @@ -180,7 +180,7 @@ public class ResourceModifiedSubmitterSvcTest { // when when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) .thenReturn(true); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())) + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())) .thenReturn(msg); when(myChannelProducer.send(any())) .thenThrow(new RuntimeException(exceptionString)); @@ -206,7 +206,7 @@ public class ResourceModifiedSubmitterSvcTest { // given // deletion fails, someone else was faster and processed the message when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(false); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // when boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity()); @@ -223,7 +223,7 @@ public class ResourceModifiedSubmitterSvcTest { public void testSubmitPersistedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){ // given when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // simulate failure writing to the channel when(myChannelProducer.send(any())).thenThrow(new MessageDeliveryException("sendingError")); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java index 670b8e07875..b80f19b9e4d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java @@ -46,6 +46,9 @@ import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nonnull; +import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.in; @@ -105,6 +108,10 @@ public class TestDaoSearch { assertSearchResultIds(theQueryUrl, theReason, hasItems(theIds)); } + public void assertSearchFinds(String theReason, String theQueryUrl, List theIds) { + assertSearchFinds(theReason, theQueryUrl, theIds.toArray(EMPTY_STRING_ARRAY)); + } + /** * Assert that the FHIR search has theIds in the search results. * @param theReason junit reason message @@ -117,6 +124,27 @@ public class TestDaoSearch { assertSearchResultIds(theQueryUrl, theReason, hasItems(bareIds)); } + public void assertSearchFindsInOrder(String theReason, String theQueryUrl, String... theIds) { + List ids = searchForIds(theQueryUrl); + + MatcherAssert.assertThat(theReason, ids, contains(theIds)); + } + + public void assertSearchFindsInOrder(String theReason, String theQueryUrl, List theIds) { + assertSearchFindsInOrder(theReason, theQueryUrl, theIds.toArray(EMPTY_STRING_ARRAY)); + } + + public void assertSearchFindsOnly(String theReason, String theQueryUrl, String... theIds) { + assertSearchIdsMatch(theReason, theQueryUrl, containsInAnyOrder(theIds)); + } + + public void assertSearchIdsMatch( + String theReason, String theQueryUrl, Matcher> theMatchers) { + List ids = searchForIds(theQueryUrl); + + MatcherAssert.assertThat(theReason, ids, theMatchers); + } + public void assertSearchResultIds(String theQueryUrl, String theReason, Matcher> matcher) { List ids = searchForIds(theQueryUrl); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java new file mode 100644 index 00000000000..1319b22b835 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java @@ -0,0 +1,74 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.search; + +import ca.uhn.fhir.jpa.dao.TestDaoSearch; +import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public interface IIdSearchTestTemplate { + TestDaoSearch getSearch(); + + ITestDataBuilder getBuilder(); + + @Test + default void testSearchByServerAssignedId_findsResource() { + IIdType id = getBuilder().createPatient(); + + getSearch().assertSearchFinds("search by server assigned id", "Patient?_id=" + id.getIdPart(), id); + } + + @Test + default void testSearchByClientAssignedId_findsResource() { + ITestDataBuilder b = getBuilder(); + b.createPatient(b.withId("client-assigned-id")); + + getSearch() + .assertSearchFinds( + "search by client assigned id", "Patient?_id=client-assigned-id", "client-assigned-id"); + } + + /** + * The _id SP is defined as token, and there is no system. + * So sorting should be string order of the value. + */ + @Test + default void testSortById_treatsIdsAsString() { + ITestDataBuilder b = getBuilder(); + b.createPatient(b.withId("client-assigned-id")); + IIdType serverId = b.createPatient(); + b.createPatient(b.withId("0-sorts-before-other-numbers")); + + getSearch() + .assertSearchFindsInOrder( + "sort by resource id", + "Patient?_sort=_id", + List.of("0-sorts-before-other-numbers", serverId.getIdPart(), "client-assigned-id")); + + getSearch() + .assertSearchFindsInOrder( + "reverse sort by resource id", + "Patient?_sort=-_id", + List.of("client-assigned-id", serverId.getIdPart(), "0-sorts-before-other-numbers")); + } +} 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 79019e41f28..9edc49122a3 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 @@ -431,6 +431,11 @@ public abstract class BaseJpaTest extends BaseTest { return deliveryLatch; } + protected void registerInterceptor(Object theInterceptor) { + myRegisteredInterceptors.add(theInterceptor); + myInterceptorRegistry.registerInterceptor(theInterceptor); + } + protected void purgeHibernateSearch(EntityManager theEntityManager) { runInTransaction(() -> { if (myFulltestSearchSvc != null && !myFulltestSearchSvc.isDisabled()) { diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java index a628e4a854d..ede47637936 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java @@ -65,6 +65,7 @@ public class ConnectionWrapper implements Connection { @Override public void commit() throws SQLException { + if (ourLog.isTraceEnabled()) { ourLog.trace("commit: {}", myWrap.hashCode()); } myWrap.commit(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java index ce598bed25b..a6e222f9c2c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java @@ -46,6 +46,7 @@ public class ConnectionWrapper implements Connection { @Override public void commit() throws SQLException { + if (ourLog.isTraceEnabled()) { ourLog.trace("Commit: {}", myWrap.hashCode()); } myWrap.commit(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java index b0bafdcdf7f..aa12360a223 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java @@ -36,7 +36,7 @@ public class HapiFhirHibernateJpaDialectTest { assertThat(outcome.getMessage(), containsString("this is a message")); try { - mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_FHIR_ID)); + mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_TYPE_FHIR_ID)); fail(); } catch (ResourceVersionConflictException e) { assertThat(e.getMessage(), containsString("The operation has failed with a client-assigned ID constraint failure")); @@ -67,7 +67,7 @@ public class HapiFhirHibernateJpaDialectTest { assertEquals("FOO", outcome.getMessage()); try { - PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_FHIR_ID)); + PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_TYPE_FHIR_ID)); mySvc.translate(exception, "a message"); fail(); } catch (ResourceVersionConflictException e) { diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java index 705205be473..c3104ab5171 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.SystemRestfulResponse; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import com.fasterxml.jackson.databind.ObjectMapper; import org.opencds.cqf.fhir.utility.Ids; @@ -39,6 +40,9 @@ public interface ICdsConfigService { @Nonnull ObjectMapper getObjectMapper(); + @Nonnull + CdsCrSettings getCdsCrSettings(); + @Nullable default DaoRegistry getDaoRegistry() { return null; diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java new file mode 100644 index 00000000000..9aa78815ce6 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java @@ -0,0 +1,37 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.config; + +import ca.uhn.fhir.cr.config.CrConfigCondition; +import ca.uhn.fhir.cr.config.RepositoryConfig; +import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * This class exists as a wrapper for the CR configs required for CDS on FHIR to be loaded only when dependencies are met. + * Adding the condition to the configs themselves causes issues with downstream projects. + * + */ +@Configuration +@Conditional(CrConfigCondition.class) +@Import({RepositoryConfig.class, ApplyOperationConfig.class}) +public class CdsCrConfig {} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java index b0c115807ee..6f679212d65 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java @@ -35,6 +35,7 @@ import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsServiceInterceptor; import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrService; import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; @@ -56,12 +57,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Optional; @Configuration +@Import(CdsCrConfig.class) public class CdsHooksConfig { private static final Logger ourLog = LoggerFactory.getLogger(CdsHooksConfig.class); @@ -128,8 +131,8 @@ public class CdsHooksConfig { } try { Constructor constructor = - clazz.get().getConstructor(RequestDetails.class, Repository.class); - return constructor.newInstance(rd, repository); + clazz.get().getConstructor(RequestDetails.class, Repository.class, ICdsConfigService.class); + return constructor.newInstance(rd, repository, theCdsConfigService); } catch (NoSuchMethodException | InvocationTargetException | InstantiationException @@ -189,9 +192,11 @@ public class CdsHooksConfig { @Bean public ICdsConfigService cdsConfigService( - FhirContext theFhirContext, @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper) { + FhirContext theFhirContext, + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + CdsCrSettings theCdsCrSettings) { return new CdsConfigServiceImpl( - theFhirContext, theObjectMapper, myDaoRegistry, myRepositoryFactory, myRestfulServer); + theFhirContext, theObjectMapper, theCdsCrSettings, myDaoRegistry, myRepositoryFactory, myRestfulServer); } @Bean diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java index 59343edff07..bf107a08166 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import com.fasterxml.jackson.databind.ObjectMapper; import javax.annotation.Nonnull; @@ -32,6 +33,7 @@ import javax.annotation.Nullable; public class CdsConfigServiceImpl implements ICdsConfigService { private final FhirContext myFhirContext; private final ObjectMapper myObjectMapper; + private final CdsCrSettings myCdsCrSettings; private final DaoRegistry myDaoRegistry; private final IRepositoryFactory myRepositoryFactory; private final RestfulServer myRestfulServer; @@ -39,11 +41,13 @@ public class CdsConfigServiceImpl implements ICdsConfigService { public CdsConfigServiceImpl( @Nonnull FhirContext theFhirContext, @Nonnull ObjectMapper theObjectMapper, + @Nonnull CdsCrSettings theCdsCrSettings, @Nullable DaoRegistry theDaoRegistry, @Nullable IRepositoryFactory theRepositoryFactory, @Nullable RestfulServer theRestfulServer) { myFhirContext = theFhirContext; myObjectMapper = theObjectMapper; + myCdsCrSettings = theCdsCrSettings; myDaoRegistry = theDaoRegistry; myRepositoryFactory = theRepositoryFactory; myRestfulServer = theRestfulServer; @@ -61,6 +65,12 @@ public class CdsConfigServiceImpl implements ICdsConfigService { return myObjectMapper; } + @Nonnull + @Override + public CdsCrSettings getCdsCrSettings() { + return myCdsCrSettings; + } + @Nullable @Override public DaoRegistry getDaoRegistry() { diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java index 17e0ea7afc5..5a6c97375ad 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java @@ -21,7 +21,16 @@ package ca.uhn.hapi.fhir.cdshooks.svc.cr; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CarePlan; import org.hl7.fhir.dstu3.model.Endpoint; @@ -60,10 +69,13 @@ import static org.opencds.cqf.fhir.utility.dstu3.Parameters.part; public class CdsCrServiceDstu3 implements ICdsCrService { protected final RequestDetails myRequestDetails; protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; protected CarePlan myResponse; protected CdsServiceResponseJson myServiceResponse; - public CdsCrServiceDstu3(RequestDetails theRequestDetails, Repository theRepository) { + public CdsCrServiceDstu3( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; myRequestDetails = theRequestDetails; myRepository = theRepository; } @@ -108,6 +120,12 @@ public class CdsCrServiceDstu3 implements ICdsCrService { endpoint.addHeader(String.format( "Authorization: %s %s", tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } } parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java index 26b0eaa464d..920f63e319f 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java @@ -22,7 +22,17 @@ package ca.uhn.hapi.fhir.cdshooks.svc.cr; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceIndicatorEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CanonicalType; @@ -61,10 +71,13 @@ import static org.opencds.cqf.fhir.utility.r4.Parameters.part; public class CdsCrServiceR4 implements ICdsCrService { protected final RequestDetails myRequestDetails; protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; protected Bundle myResponseBundle; protected CdsServiceResponseJson myServiceResponse; - public CdsCrServiceR4(RequestDetails theRequestDetails, Repository theRepository) { + public CdsCrServiceR4( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; myRequestDetails = theRequestDetails; myRepository = theRepository; } @@ -109,8 +122,13 @@ public class CdsCrServiceR4 implements ICdsCrService { endpoint.addHeader(String.format( "Authorization: %s %s", tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } } - endpoint.addHeader("Epic-Client-ID: 2cb5af9f-f483-4e2a-aedc-54c3a31cb153"); parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); } return parameters; diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java index 79055a56e1d..8f76db3a2af 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java @@ -22,7 +22,17 @@ package ca.uhn.hapi.fhir.cdshooks.svc.cr; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.hapi.fhir.cdshooks.api.json.*; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceIndicatorEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CanonicalType; @@ -61,10 +71,13 @@ import static org.opencds.cqf.fhir.utility.r5.Parameters.part; public class CdsCrServiceR5 implements ICdsCrService { protected final RequestDetails myRequestDetails; protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; protected Bundle myResponseBundle; protected CdsServiceResponseJson myServiceResponse; - public CdsCrServiceR5(RequestDetails theRequestDetails, Repository theRepository) { + public CdsCrServiceR5( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; myRequestDetails = theRequestDetails; myRepository = theRepository; } @@ -109,6 +122,12 @@ public class CdsCrServiceR5 implements ICdsCrService { endpoint.addHeader(String.format( "Authorization: %s %s", tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } } parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java new file mode 100644 index 00000000000..70258940829 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java @@ -0,0 +1,44 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 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.cdshooks.svc.cr; + +public class CdsCrSettings { + private final String DEFAULT_CLIENT_ID_HEADER_NAME = "client_id"; + private String myClientIdHeaderName; + + public static CdsCrSettings getDefault() { + CdsCrSettings settings = new CdsCrSettings(); + settings.setClientIdHeaderName(settings.DEFAULT_CLIENT_ID_HEADER_NAME); + return settings; + } + + public void setClientIdHeaderName(String theName) { + myClientIdHeaderName = theName; + } + + public String getClientIdHeaderName() { + return myClientIdHeaderName; + } + + public CdsCrSettings withClientIdHeaderName(String theName) { + myClientIdHeaderName = theName; + return this; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java index 6b55053f357..367d496bb3b 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.controller.TestServerAppCtx; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -16,6 +17,9 @@ public class TestCdsHooksConfig { return FhirContext.forR4Cached(); } + @Bean + CdsCrSettings cdsCrSettings() { return CdsCrSettings.getDefault(); } + @Bean public CdsHooksContextBooter cdsHooksContextBooter() { CdsHooksContextBooter retVal = new CdsHooksContextBooter(); diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java index ca66a5636a1..f00675fdacc 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java @@ -14,4 +14,6 @@ public abstract class BaseCrTest { @Autowired protected FhirContext myFhirContext; + @Autowired + protected CdsCrSettings myCdsCrSettings; } diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java index 05e0201284b..5b7410c2726 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java @@ -2,13 +2,37 @@ package ca.uhn.hapi.fhir.cdshooks.svc.cr; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import static ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig.CDS_HOOKS_OBJECT_MAPPER_FACTORY; + @Configuration public class TestCrConfig { @Bean FhirContext fhirContext() { return FhirContext.forR4Cached(); } + + @Bean(name = CDS_HOOKS_OBJECT_MAPPER_FACTORY) + public ObjectMapper objectMapper(FhirContext theFhirContext) { + return new CdsHooksObjectMapperFactory(theFhirContext).newMapper(); + } + + @Bean + CdsCrSettings cdsCrSettings() { return CdsCrSettings.getDefault(); } + + @Bean + public ICdsConfigService cdsConfigService( + FhirContext theFhirContext, + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + CdsCrSettings theCdsCrSettings) { + return new CdsConfigServiceImpl( + theFhirContext, theObjectMapper, theCdsCrSettings, null, null, null); + } } diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java index e0179374882..6243ab8a8bb 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java @@ -3,9 +3,11 @@ package ca.uhn.hapi.fhir.cdshooks.svc.cr.resolution; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest; import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4; import com.fasterxml.jackson.databind.ObjectMapper; @@ -25,9 +27,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class CdsCrServiceR4Test extends BaseCrTest { - private ObjectMapper myObjectMapper;@BeforeEach + private ObjectMapper myObjectMapper; + private ICdsConfigService myCdsConfigService; + + @BeforeEach public void loadJson() throws IOException { myObjectMapper = new CdsHooksObjectMapperFactory(myFhirContext).newMapper(); + myCdsConfigService = new CdsConfigServiceImpl(myFhirContext, myObjectMapper, myCdsCrSettings, null, null, null); } @Test @@ -39,7 +45,7 @@ public class CdsCrServiceR4Test extends BaseCrTest { final RequestDetails requestDetails = new SystemRequestDetails(); final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); requestDetails.setId(planDefinitionId); - final Parameters params = new CdsCrServiceR4(requestDetails, repository).encodeParams(cdsServiceRequestJson); + final Parameters params = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeParams(cdsServiceRequestJson); assertTrue(params.getParameter().size() == 3); assertTrue(params.getParameter("parameters").hasResource()); @@ -53,7 +59,7 @@ public class CdsCrServiceR4Test extends BaseCrTest { final RequestDetails requestDetails = new SystemRequestDetails(); final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); requestDetails.setId(planDefinitionId); - final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository).encodeResponse(responseBundle); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeResponse(responseBundle); assertTrue(cdsServiceResponseJson.getCards().size() == 1); assertTrue(!cdsServiceResponseJson.getCards().get(0).getSummary().isEmpty()); @@ -69,7 +75,7 @@ public class CdsCrServiceR4Test extends BaseCrTest { final RequestDetails requestDetails = new SystemRequestDetails(); final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "DischargeInstructionsPlan"); requestDetails.setId(planDefinitionId); - final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository).encodeResponse(responseBundle); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeResponse(responseBundle); assertTrue(cdsServiceResponseJson.getServiceActions().size() == 1); assertTrue(cdsServiceResponseJson.getServiceActions().get(0).getType().equals(ActionType.CREATE.toCode())); diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json index 474e666e302..422a9a05ea2 100644 --- a/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json +++ b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json @@ -2,6 +2,13 @@ "hook" : "order-sign", "hookInstance": "randomGUIDforthehookevent", "fhirServer" : "https://localhost:8000", + "fhirAuthorization": { + "access_token": "sometoken", + "token_type": "Bearer", + "expires_in": 300000, + "scope": "", + "subject": "clientIdHeaderTest" + }, "context" : { "patientId" : "Patient/123", "draftOrders" : { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 02dbd0d5ccd..550a9d28fd3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -1598,9 +1598,16 @@ public class RestfulServer extends HttpServlet implements IRestfulServer excludedParameterNames) { + String tenantId = StringUtils.defaultString(theRequest.getTenantId()); + String requestPath = StringUtils.defaultString(theRequest.getRequestPath()); + StringBuilder b = new StringBuilder(); b.append(theServerBase); + requestPath = StringUtils.substringAfter(requestPath, tenantId); - if (isNotBlank(theRequest.getRequestPath())) { - b.append('/'); - if (isNotBlank(theRequest.getTenantId()) - && theRequest.getRequestPath().startsWith(theRequest.getTenantId() + "/")) { - b.append(theRequest - .getRequestPath() - .substring(theRequest.getTenantId().length() + 1)); - } else { - b.append(theRequest.getRequestPath()); - } + if (isNotBlank(requestPath)) { + requestPath = StringUtils.prependIfMissing(requestPath, "/"); } + + b.append(requestPath); + // For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be // huge if (theRequest.getRequestType() == RequestTypeEnum.GET) { @@ -211,7 +211,6 @@ public class RestfulServerUtils { } } } - return b.toString(); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java index 95b0481c9bf..512f6b47731 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java @@ -53,6 +53,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import static ca.uhn.fhir.rest.api.Constants.URL_TOKEN_METADATA; import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_META; @@ -178,18 +180,29 @@ public class ConsentInterceptor { } } + /** + * Check if this request is eligible for cached search results. + * We can't use a cached result if consent may use canSeeResource. + * This checks for AUTHORIZED requests, and the responses from shouldProcessCanSeeResource() + * to see if this holds. + * @return may the request be satisfied from cache. + */ @Hook(value = Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH) - public boolean interceptPreCheckForCachedSearch(RequestDetails theRequestDetails) { - if (isRequestAuthorized(theRequestDetails)) { - return true; - } - return false; + public boolean interceptPreCheckForCachedSearch(@Nonnull RequestDetails theRequestDetails) { + return !isProcessCanSeeResource(theRequestDetails, null); } + /** + * Check if the search results from this request might be reused by later searches. + * We can't use a cached result if consent may use canSeeResource. + * This checks for AUTHORIZED requests, and the responses from shouldProcessCanSeeResource() + * to see if this holds. + * If not, marks the result as single-use. + */ @Hook(value = Pointcut.STORAGE_PRESEARCH_REGISTERED) public void interceptPreSearchRegistered( RequestDetails theRequestDetails, ICachedSearchDetails theCachedSearchDetails) { - if (!isRequestAuthorized(theRequestDetails)) { + if (isProcessCanSeeResource(theRequestDetails, null)) { theCachedSearchDetails.setCannotBeReused(); } } @@ -197,28 +210,10 @@ public class ConsentInterceptor { @Hook(value = Pointcut.STORAGE_PREACCESS_RESOURCES) public void interceptPreAccess( RequestDetails theRequestDetails, IPreResourceAccessDetails thePreResourceAccessDetails) { - if (isRequestAuthorized(theRequestDetails)) { - return; - } - if (isSkipServiceForRequest(theRequestDetails)) { - return; - } - if (myConsentService.isEmpty()) { - return; - } - // First check if we should be calling canSeeResource for the individual - // consent services + // Flags for each service boolean[] processConsentSvcs = new boolean[myConsentService.size()]; - boolean processAnyConsentSvcs = false; - for (int consentSvcIdx = 0; consentSvcIdx < myConsentService.size(); consentSvcIdx++) { - IConsentService nextService = myConsentService.get(consentSvcIdx); - - boolean shouldCallCanSeeResource = - nextService.shouldProcessCanSeeResource(theRequestDetails, myContextConsentServices); - processAnyConsentSvcs |= shouldCallCanSeeResource; - processConsentSvcs[consentSvcIdx] = shouldCallCanSeeResource; - } + boolean processAnyConsentSvcs = isProcessCanSeeResource(theRequestDetails, processConsentSvcs); if (!processAnyConsentSvcs) { return; @@ -262,6 +257,39 @@ public class ConsentInterceptor { } } + /** + * Is canSeeResource() active in any services? + * @param theProcessConsentSvcsFlags filled in with the responses from shouldProcessCanSeeResource each service + * @return true of any service responded true to shouldProcessCanSeeResource() + */ + private boolean isProcessCanSeeResource( + @Nonnull RequestDetails theRequestDetails, @Nullable boolean[] theProcessConsentSvcsFlags) { + if (isRequestAuthorized(theRequestDetails)) { + return false; + } + if (isSkipServiceForRequest(theRequestDetails)) { + return false; + } + if (myConsentService.isEmpty()) { + return false; + } + + if (theProcessConsentSvcsFlags == null) { + theProcessConsentSvcsFlags = new boolean[myConsentService.size()]; + } + Validate.isTrue(theProcessConsentSvcsFlags.length == myConsentService.size()); + boolean processAnyConsentSvcs = false; + for (int consentSvcIdx = 0; consentSvcIdx < myConsentService.size(); consentSvcIdx++) { + IConsentService nextService = myConsentService.get(consentSvcIdx); + + boolean shouldCallCanSeeResource = + nextService.shouldProcessCanSeeResource(theRequestDetails, myContextConsentServices); + processAnyConsentSvcs |= shouldCallCanSeeResource; + theProcessConsentSvcsFlags[consentSvcIdx] = shouldCallCanSeeResource; + } + return processAnyConsentSvcs; + } + @Hook(value = Pointcut.STORAGE_PRESHOW_RESOURCES) public void interceptPreShow(RequestDetails theRequestDetails, IPreResourceShowDetails thePreResourceShowDetails) { if (isRequestAuthorized(theRequestDetails)) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java index f073a436b0a..e44fc3d80b1 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java @@ -37,6 +37,12 @@ public class DelegatingConsentService implements IConsentService { return myTarget.startOperation(theRequestDetails, theContextServices); } + @Override + public boolean shouldProcessCanSeeResource( + RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + return myTarget.shouldProcessCanSeeResource(theRequestDetails, theContextServices); + } + @Override public ConsentOutcome canSeeResource( RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java index c98030e643a..bda74f798d7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java @@ -52,15 +52,15 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im @JsonProperty(value = "partitionId") protected RequestPartitionId myPartitionId; + @JsonProperty(value = "payloadVersion") + protected String myPayloadVersion; + @JsonIgnore protected transient IBaseResource myPayloadDecoded; @JsonIgnore protected transient String myPayloadType; - @JsonIgnore - protected String myPayloadVersion; - /** * Constructor */ @@ -68,6 +68,12 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im super(); } + public BaseResourceModifiedMessage(IIdType theIdType, OperationTypeEnum theOperationType) { + this(); + setOperationType(theOperationType); + setPayloadId(theIdType); + } + public BaseResourceModifiedMessage( FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) { this(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java index adac4d18ad9..356fc12cda6 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java @@ -26,7 +26,7 @@ public interface ITenantIdentificationStrategy { /** * Implementations should use this method to determine the tenant ID - * based on the incoming request andand populate it in the + * based on the incoming request and populate it in the * {@link RequestDetails#setTenantId(String)}. * * @param theUrlPathTokenizer The tokenizer which is used to parse the request path @@ -39,4 +39,13 @@ public interface ITenantIdentificationStrategy { * if necessary based on the tenant ID */ String massageServerBaseUrl(String theFhirServerBase, RequestDetails theRequestDetails); + + /** + * Implementations may use this method to resolve relative URL based on the tenant ID from RequestDetails. + * + * @param theRelativeUrl URL that only includes the path, e.g. "Patient/123" + * @param theRequestDetails The request details object which can be used to access tenant ID + * @return Resolved relative URL that starts with tenant ID (if tenant ID present in RequestDetails). Example: "TENANT-A/Patient/123". + */ + String resolveRelativeUrl(String theRelativeUrl, RequestDetails theRequestDetails); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java index affeaea0a12..344a515a583 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java @@ -69,7 +69,7 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio tenantId = defaultIfBlank(theUrlPathTokenizer.peek(), null); // If it's "metadata" or starts with "$", use DEFAULT partition and don't consume this token: - if (tenantId != null && (tenantId.equals("metadata") || tenantId.startsWith("$"))) { + if (tenantId != null && (tenantId.equals("metadata") || isOperation(tenantId))) { tenantId = "DEFAULT"; theRequestDetails.setTenantId(tenantId); ourLog.trace("No tenant ID found for metadata or system request; using DEFAULT."); @@ -94,6 +94,10 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio } } + private boolean isOperation(String theToken) { + return theToken.startsWith("$"); + } + @Override public String massageServerBaseUrl(String theFhirServerBase, RequestDetails theRequestDetails) { String result = theFhirServerBase; @@ -102,4 +106,29 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio } return result; } + + @Override + public String resolveRelativeUrl(String theRelativeUrl, RequestDetails theRequestDetails) { + UrlPathTokenizer tokenizer = new UrlPathTokenizer(theRelativeUrl); + // there is no more tokens in the URL - skip url resolution + if (!tokenizer.hasMoreTokens() || tokenizer.peek() == null) { + return theRelativeUrl; + } + String nextToken = tokenizer.peek(); + // there is no tenant ID in parent request details or tenant ID is already present in URL - skip url resolution + if (theRequestDetails.getTenantId() == null || nextToken.equals(theRequestDetails.getTenantId())) { + return theRelativeUrl; + } + + // token is Resource type or operation - adding tenant ID from parent request details + if (isResourceType(nextToken, theRequestDetails) || isOperation(nextToken)) { + return theRequestDetails.getTenantId() + "/" + theRelativeUrl; + } else { + return theRelativeUrl; + } + } + + private boolean isResourceType(String token, RequestDetails theRequestDetails) { + return theRequestDetails.getFhirContext().getResourceTypes().stream().anyMatch(type -> type.equals(token)); + } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java index 44ce54a368a..24bafe71c62 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java @@ -63,6 +63,7 @@ public class ServletRequestUtil { requestDetails.setRequestPath(url); requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase()); + requestDetails.setTenantId(theRequestDetails.getTenantId()); theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url); return requestDetails; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java index 9b8a159663b..d3399c06bb9 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java @@ -1,13 +1,20 @@ package ca.uhn.fhir.rest.server; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum; +import ca.uhn.fhir.rest.api.PreferHandlingEnum; +import ca.uhn.fhir.rest.api.PreferHeader; +import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -15,13 +22,19 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static ca.uhn.fhir.rest.api.RequestTypeEnum.GET; +import static ca.uhn.fhir.rest.api.RequestTypeEnum.POST; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.http.util.TextUtils.isBlank; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -149,6 +162,33 @@ public class RestfulServerUtilsTest { //Then assertThat(linkSelfWithoutGivenParameters, is(containsString("http://localhost:8000/$my-operation?"))); assertThat(linkSelfWithoutGivenParameters, is(containsString("_format=json"))); + } + @ParameterizedTest + @MethodSource("testParameters") + public void testCreateSelfLinks_withDifferentResourcePathAndTenantId(String theServerBaseUrl, String theRequestPath, + String theTenantId, String theExpectedUrl) { + //When + ServletRequestDetails servletRequestDetails = new ServletRequestDetails(); + servletRequestDetails.setRequestType(POST); + servletRequestDetails.setTenantId(StringUtils.defaultString(theTenantId)); + servletRequestDetails.setRequestPath(StringUtils.defaultString(theRequestPath)); + + //Then + String linkSelfWithoutGivenParameters = RestfulServerUtils.createLinkSelfWithoutGivenParameters(theServerBaseUrl, servletRequestDetails, null); + //Test + assertEquals(theExpectedUrl, linkSelfWithoutGivenParameters); + } + static Stream testParameters(){ + return Stream.of( + Arguments.of("http://localhost:8000/Partition-B","" ,"Partition-B","http://localhost:8000/Partition-B"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B" ,"Partition-B","http://localhost:8000/Partition-B"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B/Patient" ,"Partition-B","http://localhost:8000/Partition-B/Patient"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B/$my-operation" ,"Partition-B","http://localhost:8000/Partition-B/$my-operation"), + Arguments.of("http://localhost:8000","","","http://localhost:8000"), + Arguments.of("", "","",""), + Arguments.of("http://localhost:8000","Patient","","http://localhost:8000/Patient"), + Arguments.of("http://localhost:8000/Patient","","","http://localhost:8000/Patient") + ); } } diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 2ea85ebb887..afec2b537cb 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -139,12 +139,6 @@ ${spring-security-core.version}
    - - org.springframework.boot - spring-boot-autoconfigure - ${spring_boot_version} - - javax.servlet diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java new file mode 100644 index 00000000000..0461701e492 --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java @@ -0,0 +1,60 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 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.cr.config; + +import ca.uhn.fhir.rest.server.RestfulServer; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * The purpose of this Condition is to verify that the CR dependent beans RestfulServer and EvaluationSettings exist. + */ +public class CrConfigCondition implements Condition { + private static final Logger ourLog = LoggerFactory.getLogger(CrConfigCondition.class); + + @Override + public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { + ConfigurableListableBeanFactory beanFactory = theConditionContext.getBeanFactory(); + try { + RestfulServer bean = beanFactory.getBean(RestfulServer.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("CrConfigCondition not met: Missing RestfulServer bean"); + return false; + } + try { + EvaluationSettings bean = beanFactory.getBean(EvaluationSettings.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("CrConfigCondition not met: Missing EvaluationSettings bean"); + return false; + } + return true; + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java index 5166a5ebaf6..8504c2e2060 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfig.java @@ -23,12 +23,10 @@ import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.repo.HapiFhirRepository; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.server.RestfulServer; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration -@ConditionalOnBean(RestfulServer.class) public class RepositoryConfig { @Bean IRepositoryFactory repositoryFactory(DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java new file mode 100644 index 00000000000..caa43d8cd6c --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 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.cr.config; + +import ca.uhn.fhir.rest.server.RestfulServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class RepositoryConfigCondition implements Condition { + private static final Logger ourLog = LoggerFactory.getLogger(RepositoryConfigCondition.class); + + @Override + public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { + ConfigurableListableBeanFactory beanFactory = theConditionContext.getBeanFactory(); + try { + RestfulServer bean = beanFactory.getBean(RestfulServer.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("Unable to create bean IRepositoryFactory: Missing RestfulServer"); + return false; + } + return true; + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java index d72885a63e6..cbbcd5aa82f 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java @@ -21,36 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class ApplyOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory dstu3ActivityDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.dstu3.ActivityDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - - @Bean - ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionApplyProvider dstu3ActivityDefinitionApplyProvider() { return new ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionApplyProvider(); diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java new file mode 100644 index 00000000000..b039eb9514e --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java @@ -0,0 +1,56 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 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.cr.config.dstu3; + +import ca.uhn.fhir.cr.common.IRepositoryFactory; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CrProcessorConfig { + @Bean + ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory dstu3ActivityDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.dstu3.ActivityDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory dstu3QuestionnaireResponseProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.dstu3.QuestionnaireResponseProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java index 773e57da71c..6dd20561530 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java @@ -21,29 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class ExtractOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory dstu3QuestionnaireResponseProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.dstu3.QuestionnaireResponseProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaireresponse.QuestionnaireResponseExtractProvider dstu3QuestionnaireResponseExtractProvider() { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java index 1617dfdfcbf..0c6d37040cd 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java @@ -21,41 +21,25 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class PackageOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionPackageProvider dstu3PlanDefinitionPackageProvider() { return new ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionPackageProvider(); } - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePackageProvider dstu3QuestionnairePackageProvider() { return new ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePackageProvider(); diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java index bccaab2d578..8cb3da6c55b 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java @@ -21,29 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class PopulateOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePopulateProvider dstu3QuestionnairePopulateProvider() { return new ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePopulateProvider(); diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java index 89d732b54fd..a4cb102e45b 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java @@ -21,36 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class ApplyOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory r4ActivityDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.r4.ActivityDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - - @Bean - ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionApplyProvider r4ActivityDefinitionApplyProvider() { return new ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionApplyProvider(); diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java new file mode 100644 index 00000000000..6632a244c39 --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java @@ -0,0 +1,56 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 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.cr.config.r4; + +import ca.uhn.fhir.cr.common.IRepositoryFactory; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CrProcessorConfig { + @Bean + ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory r4ActivityDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.r4.ActivityDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory r4QuestionnaireResponseProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.r4.QuestionnaireResponseProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java index a33f1267742..45a031dd910 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java @@ -21,29 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class ExtractOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory r4QuestionnaireResponseProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.r4.QuestionnaireResponseProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaireresponse.QuestionnaireResponseExtractProvider r4QuestionnaireResponseExtractProvider() { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java index 7429a0dfe53..ffbd0c29dfc 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java @@ -21,41 +21,25 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class PackageOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionPackageProvider r4PlanDefinitionPackageProvider() { return new ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionPackageProvider(); } - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePackageProvider r4QuestionnairePackageProvider() { return new ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePackageProvider(); diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java index da10bb376f6..87cdc729be3 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java @@ -21,29 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; @Configuration -@ConditionalOnBean({IRepositoryFactory.class, RestfulServer.class, EvaluationSettings.class}) +@Import(CrProcessorConfig.class) public class PopulateOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePopulateProvider r4QuestionnairePopulateProvider() { return new ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePopulateProvider(); diff --git a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java index 625addbd379..012a2856ca9 100644 --- a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java +++ b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java @@ -39,7 +39,8 @@ import org.springframework.context.annotation.Configuration; /** * Implements ITestDataBuilder via a live DaoRegistry. - * + * Note: this implements {@link AfterEachCallback} and will delete any resources created when registered + * via {@link org.junit.jupiter.api.extension.RegisterExtension}. * Add the inner {@link Config} to your spring context to inject this. * For convenience, you can still implement ITestDataBuilder on your test class, and delegate the missing methods to this bean. */ @@ -75,7 +76,9 @@ public class DaoTestDataBuilder implements ITestDataBuilder.WithSupport, ITestDa //noinspection rawtypes IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); //noinspection unchecked - return dao.update(theResource, mySrd).getId().toUnqualifiedVersionless(); + IIdType id = dao.update(theResource, mySrd).getId().toUnqualifiedVersionless(); + myIds.put(theResource.fhirType(), id); + return id; } @Override diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java index 0ff58ec7cf3..bf32c4a758f 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java @@ -251,7 +251,7 @@ public class BinaryStorageInterceptor> { } if (myBinaryStorageSvc.isValidBlobId(newBlobId)) { List deferredBinaryTargets = - getOrCreateDeferredBinaryStorageMap(theTransactionDetails); + getOrCreateDeferredBinaryStorageList(theResource); DeferredBinaryTarget newDeferredBinaryTarget = new DeferredBinaryTarget(newBlobId, nextTarget, data); deferredBinaryTargets.add(newDeferredBinaryTarget); @@ -289,21 +289,29 @@ public class BinaryStorageInterceptor> { } @Nonnull - private List getOrCreateDeferredBinaryStorageMap(TransactionDetails theTransactionDetails) { - return theTransactionDetails.getOrCreateUserData(getDeferredListKey(), ArrayList::new); + @SuppressWarnings("unchecked") + private List getOrCreateDeferredBinaryStorageList(IBaseResource theResource) { + Object deferredBinaryTargetList = theResource.getUserData(getDeferredListKey()); + if (deferredBinaryTargetList == null) { + deferredBinaryTargetList = new ArrayList<>(); + theResource.setUserData(getDeferredListKey(), deferredBinaryTargetList); + } + return (List) deferredBinaryTargetList; } + @SuppressWarnings("unchecked") @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED) public void storeLargeBinariesBeforeCreatePersistence( - TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePoincut) + TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException { - if (theTransactionDetails == null) { + if (theResource == null) { return; } - List deferredBinaryTargets = theTransactionDetails.getUserData(getDeferredListKey()); - if (deferredBinaryTargets != null) { + Object deferredBinaryTargetList = theResource.getUserData(getDeferredListKey()); + + if (deferredBinaryTargetList != null) { IIdType resourceId = theResource.getIdElement(); - for (DeferredBinaryTarget next : deferredBinaryTargets) { + for (DeferredBinaryTarget next : (List) deferredBinaryTargetList) { String blobId = next.getBlobId(); IBinaryTarget target = next.getBinaryTarget(); InputStream dataStream = next.getDataStream(); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index 50d4669be68..56395199c9f 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -514,7 +514,8 @@ public class HapiTransactionService implements IHapiTransactionService { } @Nullable - private static T executeInExistingTransaction(TransactionCallback theCallback) { + private static T executeInExistingTransaction(@Nonnull TransactionCallback theCallback) { + // TODO we could probably track the TransactionStatus we need as a thread local like we do our partition id. return theCallback.doInTransaction(null); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java new file mode 100644 index 00000000000..daac9c18948 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java @@ -0,0 +1,35 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.subscription.channel.api; + +/** + * This exception represents the message payload exceeded the maximum message size of the broker. Used as a wrapper of + * similar exceptions specific to different message brokers, e.g. kafka.common.errors.RecordTooLargeException. + */ +public class PayloadTooLargeException extends RuntimeException { + + public PayloadTooLargeException(String theMessage) { + super(theMessage); + } + + public PayloadTooLargeException(String theMessage, Throwable theThrowable) { + super(theMessage, theThrowable); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java index 9cd638d2600..04cf84a0cc6 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java @@ -108,6 +108,10 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes myPayloadId = thePayload.getIdElement().toUnqualifiedVersionless().getValue(); } + public void setPayloadToNull() { + myPayloadString = null; + } + @Override public String getPayloadId() { return myPayloadId; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java index ab4fe6c7de9..e493035f80c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; /** * Most of this class has been moved to ResourceModifiedMessage in the hapi-fhir-server project, for a reusable channel ResourceModifiedMessage @@ -47,6 +48,11 @@ public class ResourceModifiedMessage extends BaseResourceModifiedMessage { super(); } + public ResourceModifiedMessage(IIdType theIdType, OperationTypeEnum theOperationType) { + super(theIdType, theOperationType); + setPartitionId(RequestPartitionId.defaultPartition()); + } + public ResourceModifiedMessage( FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) { super(theFhirContext, theResource, theOperationType); @@ -79,6 +85,10 @@ public class ResourceModifiedMessage extends BaseResourceModifiedMessage { mySubscriptionId = theSubscriptionId; } + public void setPayloadToNull() { + myPayload = null; + } + @Override public String toString() { return new ToStringBuilder(this) diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java index 68aad03a48c..5f54b04e544 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.model.entity.IPersistedResourceModifiedMessagePK; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import java.util.List; +import java.util.Optional; /** * An implementer of this interface will provide {@link ResourceModifiedMessage} persistence services. @@ -61,10 +62,29 @@ public interface IResourceModifiedMessagePersistenceSvc { /** * Restore a resourceModifiedMessage to its pre persistence representation. * - * @param thePersistedResourceModifiedMessage The message needing restoration. + * @param theResourceModifiedMessage The message needing restoration. * @return The resourceModifiedMessage in its pre persistence form. */ - ResourceModifiedMessage inflatePersistedResourceModifiedMessage( + ResourceModifiedMessage inflatePersistedResourceModifiedMessage(ResourceModifiedMessage theResourceModifiedMessage); + + /** + * Restore a resourceModifiedMessage to its pre persistence representation or null if the resource does not exist. + * + * @param theResourceModifiedMessage + * @return An Optional containing The resourceModifiedMessage in its pre persistence form or null when the resource + * does not exist + */ + Optional inflatePersistedResourceModifiedMessageOrNull( + ResourceModifiedMessage theResourceModifiedMessage); + + /** + * Create a ResourceModifiedMessage without its pre persistence representation, i.e. without the resource body in + * payload + * + * @param thePersistedResourceModifiedMessage The message needing creation + * @return The resourceModifiedMessage without its pre persistence form + */ + ResourceModifiedMessage createResourceModifiedMessageFromEntityWithoutInflation( IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage); /** diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java index 0d20454ddae..ea370b70b12 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java @@ -55,9 +55,9 @@ public class RDFParserR4Test { @prefix xsd: . - rdf:type fhir:Patient ; - fhir:Patient.active [ fhir:value true ] ; - fhir:Resource.id [ fhir:value "123" ] ; + rdf:type fhir:Patient; + fhir:Patient.active [ fhir:value true ]; + fhir:Resource.id [ fhir:value "123" ]; fhir:nodeRole fhir:treeRoot . """; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java index a27b8ae4134..784bd9d68c9 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java @@ -18,8 +18,8 @@ import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome; import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService; import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.rest.server.util.ICachedSearchDetails; import ca.uhn.fhir.test.utilities.HttpClientExtension; -import ca.uhn.fhir.test.utilities.LoggingExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import com.google.common.base.Charsets; import com.helger.commons.collection.iterate.EmptyEnumeration; @@ -36,6 +36,7 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; @@ -61,8 +62,11 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -75,15 +79,13 @@ public class ConsentInterceptorTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ConsentInterceptorTest.class); @RegisterExtension private final HttpClientExtension myClient = new HttpClientExtension(); - @RegisterExtension - private final LoggingExtension myLoggingExtension = new LoggingExtension(); private static final FhirContext ourCtx = FhirContext.forR4Cached(); private int myPort; private static final DummyPatientResourceProvider ourPatientProvider = new DummyPatientResourceProvider(ourCtx); private static final DummySystemProvider ourSystemProvider = new DummySystemProvider(); @RegisterExtension - private static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) .registerProvider(ourPatientProvider) .registerProvider(ourSystemProvider) .withPagingProvider(new FifoMemoryPagingProvider(10)); @@ -357,9 +359,7 @@ public class ConsentInterceptorTest { when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); when(myConsentSvc.canSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> ConsentOutcome.PROCEED); - when(myConsentSvc.willSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> { - return ConsentOutcome.REJECT; - }); + when(myConsentSvc.willSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> ConsentOutcome.REJECT); HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient"); @@ -861,7 +861,7 @@ public class ConsentInterceptorTest { @Test - public void testNoServicesRegistered() throws IOException { + public void testNoServicesRegistered() { myInterceptor.unregisterConsentService(myConsentSvc); Patient patientA = new Patient(); @@ -886,6 +886,52 @@ public class ConsentInterceptorTest { assertEquals(2, response.getTotal()); } + @Nested class CacheUsage { + @Mock ICachedSearchDetails myCachedSearchDetails; + ServletRequestDetails myRequestDetails = new ServletRequestDetails(); + + @Test + void testAuthorizedRequestsMayBeCachedAndUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.AUTHORIZED); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertTrue(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "AUTHORIZED requests can use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails, never()).setCannotBeReused(); + } + + @Test + void testCanSeeResourceFilteredRequestsMayNotBeCachedNorUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); + when(myConsentSvc2.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc2.shouldProcessCanSeeResource(any(), any())).thenReturn(false); + myInterceptor.registerConsentService(myConsentSvc2); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertFalse(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "PROCEED requests can not use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails).setCannotBeReused(); + } + + @Test + void testRequestsWithNoCanSeeFilteringMayBeCachedAndUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.shouldProcessCanSeeResource(any(), any())).thenReturn(false); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertTrue(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "PROCEED requests that promise not to filter can not use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails, never()).setCannotBeReused(); + } + } + + + public static class DummyPatientResourceProvider extends HashMapResourceProvider { public DummyPatientResourceProvider(FhirContext theFhirContext) { diff --git a/hapi-fhir-test-utilities/src/test/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategyTest.java b/hapi-fhir-test-utilities/src/test/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategyTest.java index fcad982df60..4e96c19dc18 100644 --- a/hapi-fhir-test-utilities/src/test/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategyTest.java +++ b/hapi-fhir-test-utilities/src/test/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategyTest.java @@ -11,12 +11,17 @@ import ca.uhn.fhir.util.UrlPathTokenizer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Collections; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -69,6 +74,27 @@ public class UrlBaseTenantIdentificationStrategyTest { assertEquals(BASE_URL, actual); } + @CsvSource(value = { + " , , empty input url - empty URL should be returned", + "TENANT1/Patient/123 , TENANT1/Patient/123 , tenant ID already exists - input URL should be returned", + "TENANT1/Patient/$export, TENANT1/Patient/$export , tenant ID already exists - input URL should be returned", + "TENANT2/Patient/123 , TENANT2/Patient/123 , requestDetails contains different tenant ID - input URL should be returned", + "TENANT2/$export , TENANT2/$export , requestDetails contains different tenant ID - input URL should be returned", + "Patient/123 , TENANT1/Patient/123 , url starts with resource type - tenant ID should be added to URL", + "Patient/$export , TENANT1/Patient/$export , url starts with resource type - tenant ID should be added to URL", + "$export , TENANT1/$export , url starts with operation name - tenant ID should be added to URL", + }) + @ParameterizedTest + void resolveRelativeUrl_returnsCorrectlyResolvedUrl(String theInputUrl, String theExpectedResolvedUrl, String theMessage) { + lenient().when(myRequestDetails.getTenantId()).thenReturn("TENANT1"); + lenient().when(myFHIRContext.getResourceTypes()).thenReturn(Collections.singleton("Patient")); + lenient().when(myRequestDetails.getFhirContext()).thenReturn(myFHIRContext); + + String actual = ourTenantStrategy.resolveRelativeUrl(theInputUrl, myRequestDetails); + + assertEquals(theExpectedResolvedUrl, actual, theMessage); + } + @Test void extractTenant_givenNormalRequestAndExplicitTenant_shouldUseTenant() { //given a Patient request on MYTENANT diff --git a/pom.xml b/pom.xml index 41eed268323..d3ed41f8e6e 100644 --- a/pom.xml +++ b/pom.xml @@ -897,7 +897,7 @@ - 6.1.2 + 6.1.2.2 2.37.0 -Dfile.encoding=UTF-8 -Xmx2048m @@ -934,9 +934,9 @@ 2.3.1 2.3.0.1 3.0.0 - 4.8.0 + 4.9.0 3.0.3 - 10.0.14 + 10.0.17 3.0.2 5.9.1 0.64.8 @@ -983,7 +983,7 @@ 1.0.8 - 3.0.0-PRE8 + 3.0.0-PRE9 5.4.1 @@ -1171,7 +1171,7 @@ com.squareup.okio okio-jvm - 3.2.0 + 3.4.0 @@ -1385,7 +1385,7 @@ com.mysql mysql-connector-j - 8.0.32 + 8.2.0 org.springdoc @@ -1871,7 +1871,7 @@ --> org.elasticsearch.client elasticsearch-rest-high-level-client - 7.17.3 + 7.17.13 com.fasterxml.jackson.dataformat From 9dace159b4c004ba10fe9f1b3fe09f3c972b26fd Mon Sep 17 00:00:00 2001 From: Aditya Dave Date: Mon, 13 Nov 2023 14:50:11 -0500 Subject: [PATCH 28/86] 5426 enhance bundleutil (#5451) * failing test * fix and more tests * cleanup * spotless * remove class paths * add changelog --------- Co-authored-by: aditya_dave --- .../java/ca/uhn/fhir/util/BundleUtil.java | 21 +++++++ .../7_0_0/5426-enhance-bundleutil.yaml | 4 ++ .../uhn/fhir/util/bundle/BundleUtilTest.java | 55 ++++++++++++++++++- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index 2ed1a3b47af..516c5c078b7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -39,6 +39,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; @@ -53,6 +54,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.hl7.fhir.instance.model.api.IBaseBundle.LINK_PREV; @@ -693,6 +695,25 @@ public class BundleUtil { return bundleEntry; } + /** + * Get resource from bundle by resource type and reference + * @param theContext FhirContext + * @param theBundle IBaseBundle + * @param theReference IBaseReference + * @return IBaseResource if found and null if not found. + */ + @Nonnull + public static IBaseResource getResourceByReferenceAndResourceType( + @Nonnull FhirContext theContext, @Nonnull IBaseBundle theBundle, @Nonnull IBaseReference theReference) { + return toListOfResources(theContext, theBundle).stream() + .filter(theResource -> theReference + .getReferenceElement() + .getIdPart() + .equals(theResource.getIdElement().getIdPart())) + .findFirst() + .orElse(null); + } + private static class SortLegality { private boolean myIsLegal; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml new file mode 100644 index 00000000000..918a8283ddb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5426 +title: "Added a new method `getResourceByReferenceAndResourceType()` in `BundleUtil.java` to find a specific `Resource` from `Bundle` using `Reference`." diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java index 4d0197106fd..9af769d8fbf 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java @@ -9,7 +9,10 @@ import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Claim; +import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.ExplanationOfBenefit; import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.Observation; @@ -17,11 +20,13 @@ import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.UriType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import javax.annotation.Nonnull; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -47,6 +52,7 @@ import static org.junit.jupiter.api.Assertions.fail; public class BundleUtilTest { private static final FhirContext ourCtx = FhirContext.forR4Cached(); + public static final String PATIENT_REFERENCE = "Patient/123"; @Nested @@ -365,7 +371,7 @@ public class BundleUtilTest { @Test public void testBundleSortsCanHandlesDeletesThatContainNoResources() { Patient p = new Patient(); - p.setId("Patient/123"); + p.setId(PATIENT_REFERENCE); BundleBuilder builder = new BundleBuilder(ourCtx); builder.addTransactionDeleteEntry(p); BundleUtil.sortEntriesIntoProcessingOrder(ourCtx, builder.getBundle()); @@ -525,6 +531,53 @@ public class BundleUtilTest { return -1; } + @Test + public void testGetResourceByReferenceAndResourceTypeReturnsResourceIfFound() { + // setup + final Patient expected = withPatient("123"); + final Bundle bundle = withBundle(expected); + final Reference reference = new Reference(PATIENT_REFERENCE); + // execute + final IBaseResource actual = BundleUtil.getResourceByReferenceAndResourceType(ourCtx, bundle, reference); + // validate + assertEquals(expected, actual); + } + + @Test + public void testGetResourceByReferenceAndResourceTypeReturnsNullIfResourceNotFound() { + // setup + final Patient patient = withPatient("ABC"); + final Bundle bundle = withBundle(patient); + final Reference reference = new Reference(PATIENT_REFERENCE); + // execute + final IBaseResource actual = BundleUtil.getResourceByReferenceAndResourceType(ourCtx, bundle, reference); + // validate + assertNull(actual); + } + + @Nonnull + private static Bundle withBundle(Resource theResource) { + final Bundle bundle = new Bundle(); + bundle.addEntry(withBundleEntryComponent(theResource)); + bundle.addEntry(withBundleEntryComponent(new Coverage())); + bundle.addEntry(withBundleEntryComponent(new Claim())); + return bundle; + } + + @Nonnull + private static Bundle.BundleEntryComponent withBundleEntryComponent(Resource theResource) { + final Bundle.BundleEntryComponent bundleEntryComponent = new Bundle.BundleEntryComponent(); + bundleEntryComponent.setResource(theResource); + return bundleEntryComponent; + } + + @Nonnull + private static Patient withPatient(@Nonnull String theResourceId) { + final Patient patient = new Patient(); + patient.setId(theResourceId); + return patient; + } + @AfterAll public static void afterClassClearContext() { TestUtil.randomizeLocaleAndTimezone(); From 41d9abf6acf7772a17afb7aef8d9ed8d4a246d5a Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:53:49 -0500 Subject: [PATCH 29/86] Resolve 5452-mdm-query-link-returns-scientific-notation-in-linkcreated-and-linkupdated (#5453) * set the scale to 0 when transforming double to BigDecimal * - set negative scale to 0 when transforming double to BigDecimal - added test --- .../java/ca/uhn/fhir/util/ParametersUtil.java | 11 +++++++++-- ...notation-in-linkCreated-and-linkUpdated.yaml | 5 +++++ .../ca/uhn/fhir/util/ParametersUtilR4Test.java | 17 +++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java index 44d26280759..c7ffd7cb439 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java @@ -403,8 +403,15 @@ public class ParametersUtil { public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) { IPrimitiveType value = (IPrimitiveType) theContext.getElementDefinition("decimal").newInstance(); - value.setValue(theValue == null ? null : BigDecimal.valueOf(theValue)); - + if (theValue == null) { + value.setValue(null); + } else { + BigDecimal decimalValue = BigDecimal.valueOf(theValue); + if (decimalValue.scale() < 0) { + decimalValue = decimalValue.setScale(0); + } + value.setValue(decimalValue); + } addPart(theContext, theParameter, theName, value); } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml new file mode 100644 index 00000000000..9221fe51456 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5452 +title: "Previously, the $mdm-query-link operation would return values of field linkCreated and linkUpdated in scientific +notation when the last digits are 0. This is now fixed and always returns in standard notation." diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/ParametersUtilR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/ParametersUtilR4Test.java index 7e03c8097e6..f8c90c4e453 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/ParametersUtilR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/ParametersUtilR4Test.java @@ -9,6 +9,7 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; import java.util.List; import java.util.Optional; @@ -79,4 +80,20 @@ public class ParametersUtilR4Test { assertThat(values.get(1), is(TEST_PERSON_ID)); assertThat(values.get(2), is(TEST_PERSON_ID)); } + + @Test + public void testAddPartDecimalNoScientificNotation() { + // setup + Double decimalValue = Double.valueOf("10000000"); + IBaseParameters parameters = ParametersUtil.newInstance(ourFhirContext); + IBase resultPart = ParametersUtil.addParameterToParameters(ourFhirContext, parameters, "link"); + + // execute + ParametersUtil.addPartDecimal(ourFhirContext, resultPart, "linkCreated", decimalValue); + + // verify + String expected = BigDecimal.valueOf(decimalValue).toPlainString(); + List results = ParametersUtil.getNamedParameterPartAsString(ourFhirContext, parameters, "link", "linkCreated"); + assertEquals(expected, results.get(0)); + } } From 1412873caca04a33cd307206da883f6cef6ccf18 Mon Sep 17 00:00:00 2001 From: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Date: Thu, 16 Nov 2023 07:18:35 -0500 Subject: [PATCH 30/86] Providing parameter '_total' when searching may lead to paging issues. (#5455) * Initial failing test. * Initial failing test. * fix and changelog. * applying spotless check. * small code refactoring. * test refactoring --------- Co-authored-by: peartree --- ...tal-interfers-with-bundle-pagination-.yaml | 5 +++ .../ca/uhn/fhir/jpa/config/JpaConfig.java | 3 +- .../search/PersistedJpaBundleProvider.java | 5 +++ .../PersistedJpaBundleProviderFactory.java | 2 - ...istedJpaSearchFirstPageBundleProvider.java | 7 +--- .../jpa/search/SearchCoordinatorSvcImpl.java | 9 +++- .../search/SearchCoordinatorSvcImplTest.java | 9 ++-- .../FhirResourceDaoR4SearchOptimizedTest.java | 41 +++++++++++++++++++ 8 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml new file mode 100644 index 00000000000..50ae0efceae --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5454 +jira: SMILE-7295 +title: "Previously, searching with parameter '_total' could influence chunked query resultsets and subsequently, paged results. This is now fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index c6a27252a95..50ec76efa26 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -572,12 +572,11 @@ public class JpaConfig { @Scope("prototype") public PersistedJpaSearchFirstPageBundleProvider newPersistedJpaSearchFirstPageBundleProvider( RequestDetails theRequest, - Search theSearch, SearchTask theSearchTask, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { return new PersistedJpaSearchFirstPageBundleProvider( - theSearch, theSearchTask, theSearchBuilder, theRequest, theRequestPartitionId); + theSearchTask, theSearchBuilder, theRequest, theRequestPartitionId); } @Bean(name = RepositoryValidatingRuleBuilder.REPOSITORY_VALIDATING_RULE_BUILDER) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index a02d48baf20..5d2dae49723 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -151,6 +151,11 @@ public class PersistedJpaBundleProvider implements IBundleProvider { myRequestPartitionHelperSvc = theRequestPartitionHelperSvc; } + @VisibleForTesting + public Search getSearchEntityForTesting() { + return getSearchEntity(); + } + protected Search getSearchEntity() { return mySearchEntity; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java index 5e89f521a09..6b9f48fc86e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java @@ -55,14 +55,12 @@ public class PersistedJpaBundleProviderFactory { public PersistedJpaSearchFirstPageBundleProvider newInstanceFirstPage( RequestDetails theRequestDetails, - Search theSearch, SearchTask theTask, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { return (PersistedJpaSearchFirstPageBundleProvider) myApplicationContext.getBean( JpaConfig.PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER, theRequestDetails, - theSearch, theTask, theSearchBuilder, theRequestPartitionId); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java index 7c61be7d556..b1ac13fcbe2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java @@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.search; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.dao.ISearchBuilder; -import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.entity.SearchTypeEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; @@ -52,16 +51,14 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl */ @SuppressWarnings("rawtypes") public PersistedJpaSearchFirstPageBundleProvider( - Search theSearch, SearchTask theSearchTask, ISearchBuilder theSearchBuilder, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) { - super(theRequest, theSearch.getUuid()); + super(theRequest, theSearchTask.getSearch()); - assert theSearch.getSearchType() != SearchTypeEnum.HISTORY; + assert getSearchEntity().getSearchType() != SearchTypeEnum.HISTORY; - setSearchEntity(theSearch); mySearchTask = theSearchTask; mySearchBuilder = theSearchBuilder; super.setRequestPartitionId(theRequestPartitionId); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 1ef70a8b469..c22c3b65fbc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -110,7 +110,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { private final SearchStrategyFactory mySearchStrategyFactory; private final ExceptionService myExceptionSvc; private final BeanFactory myBeanFactory; - private final ConcurrentHashMap myIdToSearchTask = new ConcurrentHashMap<>(); + private ConcurrentHashMap myIdToSearchTask = new ConcurrentHashMap<>(); private final Consumer myOnRemoveSearchTask = myIdToSearchTask::remove; @@ -162,6 +162,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { return myIdToSearchTask.keySet(); } + @VisibleForTesting + public void setIdToSearchTaskMapForUnitTests(ConcurrentHashMap theIdToSearchTaskMap) { + myIdToSearchTask = theIdToSearchTaskMap; + } + @VisibleForTesting public void setLoadingThrottleForUnitTests(Integer theLoadingThrottleForUnitTests) { myLoadingThrottleForUnitTests = theLoadingThrottleForUnitTests; @@ -571,7 +576,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { task.call(); PersistedJpaSearchFirstPageBundleProvider retVal = myPersistedJpaBundleProviderFactory.newInstanceFirstPage( - theRequestDetails, theSearch, task, theSb, theRequestPartitionId); + theRequestDetails, task, theSb, theRequestPartitionId); ourLog.debug("Search initial phase completed in {}ms", w.getMillis()); return retVal; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index a3e94a48969..5d9c438b7e2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -293,12 +293,11 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc { } private void initAsyncSearches() { - when(myPersistedJpaBundleProviderFactory.newInstanceFirstPage(nullable(RequestDetails.class), nullable(Search.class), nullable(SearchTask.class), nullable(ISearchBuilder.class), nullable(RequestPartitionId.class))).thenAnswer(t -> { + when(myPersistedJpaBundleProviderFactory.newInstanceFirstPage(nullable(RequestDetails.class), nullable(SearchTask.class), nullable(ISearchBuilder.class), nullable(RequestPartitionId.class))).thenAnswer(t -> { RequestDetails requestDetails = t.getArgument(0, RequestDetails.class); - Search search = t.getArgument(1, Search.class); - SearchTask searchTask = t.getArgument(2, SearchTask.class); - ISearchBuilder searchBuilder = t.getArgument(3, ISearchBuilder.class); - PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(search, searchTask, searchBuilder, requestDetails, null); + SearchTask searchTask = t.getArgument(1, SearchTask.class); + ISearchBuilder searchBuilder = t.getArgument(2, ISearchBuilder.class); + PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(searchTask, searchBuilder, requestDetails, null); retVal.setStorageSettingsForUnitTest(new JpaStorageSettings()); retVal.setTxServiceForUnitTest(myTransactionService); retVal.setSearchCoordinatorSvcForUnitTest(mySvc); 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 3972c959dda..0f63fe5d3b5 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 @@ -8,7 +8,9 @@ import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; +import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; +import ca.uhn.fhir.jpa.search.builder.tasks.SearchTask; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; @@ -29,7 +31,9 @@ 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; @@ -51,6 +55,8 @@ import org.hl7.fhir.r4.model.UriType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; @@ -58,6 +64,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.stream.Collectors; @@ -102,6 +109,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { public final void after() { mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(QueryParameterUtils.DEFAULT_SYNC_SIZE); + mySearchCoordinatorSvcImpl.setIdToSearchTaskMapForUnitTests(new ConcurrentHashMap<>()); myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds()); myCaptureQueriesListener.setCaptureQueryStackTrace(false); myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields()); @@ -214,6 +222,39 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { } + @Test + public void testSearchCoordinatorSvc_whenExecutingSearchWithParamTotal_returnsBundleSynchronizedWithBackingSearchCapabilities(){ + ArgumentCaptor keyArgumentCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueArgumentCaptor = ArgumentCaptor.forClass(SearchTask.class); + ConcurrentHashMap spyingIdToSearchTaskMap = Mockito.spy(new ConcurrentHashMap<>()); + mySearchCoordinatorSvcImpl.setIdToSearchTaskMapForUnitTests(spyingIdToSearchTaskMap); + create200Patients(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_NAME, new StringParam("FAM")); + params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); + + // calling dao.search will end up invoking the searchCoordinatorSvc. based on the provided search parameters, the svc + // generates and triggers a searchTask which will create chunked resultsets. the searchTask make use of a searchEntity + // to keep track of search progress and key indicators like the search total count. + PersistedJpaSearchFirstPageBundleProvider results = (PersistedJpaSearchFirstPageBundleProvider) myPatientDao.search(params); + + // to return the correct resources through method getResources(), the PersistedJpaSearchFirstPageBundleProvider generated by the + // searchCoordinatorSvc needs to access the same searchEntity that was used by the searchTask. this test ensures that the searchEntity + // operated upon by the searchTask is the same as the searchEntity that is found in the generated PersistedJpaSearchFirstPageBundleProvider. + Mockito.verify(spyingIdToSearchTaskMap, Mockito.times(1)).put(keyArgumentCaptor.capture(), valueArgumentCaptor.capture()); + + Search bundleProviderSearch = results.getSearchEntityForTesting(); + Search backingSearch = valueArgumentCaptor.getValue().getSearch(); + + assertThat(bundleProviderSearch.getUuid(), equalTo(keyArgumentCaptor.getValue())); + assertThat(bundleProviderSearch.getUuid(), equalTo(backingSearch.getUuid())); + + assertThat(bundleProviderSearch.getStatus(), equalTo(backingSearch.getStatus())); + assertThat(bundleProviderSearch.getId(), equalTo(backingSearch.getId())); + + } + @Test public void testFetchTotalAccurateForSlowLoading() { create200Patients(); From 7efea956ee2cd386f4c8074b73bc3073d6d3bc0f Mon Sep 17 00:00:00 2001 From: michaelabuckley Date: Thu, 16 Nov 2023 12:06:20 -0500 Subject: [PATCH 31/86] Document the hfj_spidx_* tables. (#5456) --- .../uhn/hapi/fhir/docs/server_jpa/schema.md | 312 +++++++++++++++++- 1 file changed, 309 insertions(+), 3 deletions(-) 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 46310f9c51f..8bdc247c5d6 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 @@ -262,6 +262,11 @@ a [Resource Client ID Strategy](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir 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 @@ -526,6 +531,15 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**. This is the name of the resource being indexed. + + + + + + + @@ -550,12 +564,13 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**. # HFJ_SPIDX_DATE: Date Search Parameters -For any FHIR Search Parameter of type *date* that generates a database index, a row in the *HFJ_SPIDX_DATE* table will be created. +For any FHIR Search Parameter of type [*date*](https://www.hl7.org/fhir/search.html#date) that generates a database index, a row in the `HFJ_SPIDX_DATE` table will be created. +Range queries with Date parameters (e.g. `Observation?date=ge2020-01-01`) will query the HASH_IDENTITY, SP_VALUE_LOW_DATE_ORDINAL and/or SP_VALUE_HIGH_DATE_ORDINAL columns. +Range queries with DateTime parameters (e.g. `Observation?date=ge2021-01-01T10:30:00`) will query the HASH_IDENTITY, SP_VALUE_LOW and/or SP_VALUE_HIGH columns. +Sorting is done by the SP_VALUE_LOW column. ## Columns -The following columns are common to **all HFJ_SPIDX_xxx tables**. -
    HASH_IDENTITYLong + A hash of SP_NAME and RES_TYPE. Used to narrow the table to a specific SearchParameter during sorting, and some queries. +
    SP_UPDATED
    @@ -617,3 +632,294 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**.
    + +# HFJ_SPIDX_NUMBER: Number Search Parameters + +FHIR Search Parameters of type [*number*](https://www.hl7.org/fhir/search.html#number) produce rows in the `HFJ_SPIDX_NUMBER` table. +Range queries and sorting use the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    SP_VALUEDoubleNot nullable + This is the value extracted by the SearchParameter expression. +
    + + + +# HFJ_SPIDX_QUANTITY: Quantity Search Parameters + +FHIR Search Parameters of type [*quantity*](https://www.hl7.org/fhir/search.html#quantity) produce rows in the `HFJ_SPIDX_QUANTITY` table. +Range queries (e.g. `Observation?valueQuantity=gt100`) with no units provided will query the HASH_IDENTITY and SP_VALUE columns. +Range queries (e.g. `Observation?valueQuantity=gt100||mmHg`) with a unit but not unit-sytem provided will use the HASH_IDENTITY_AND_UNITS and SP_VALUE columns. +Range queries (e.g. `Observation?valueQuantity=gt100|http://unitsofmeasure.org|mmHg`) with a full system and unit will use the HASH_IDENTITY_SYS_UNITS and SP_VALUE columns. +Sorting is done via the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_IDENTITY_AND_UNITSLong + A hash like HASH_IDENTITY that also includes the SP_UNITS column. +
    HASH_IDENTITY_SYS_UNITSLong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM and SP_UNITS columns. +
    SP_SYSTEMString + The system of the quantity units. e.g. "http://unitsofmeasure.org". +
    SP_UNITSString + The units of the quantity. E.g. "mg". +
    SP_VALUEDouble + This is the value extracted by the SearchParameter expression. +
    + +# HFJ_SPIDX_QUANTITY_NRML: Normalized Quantity Search Parameters + +Hapi Fhir supports searching by normalized units when enabled (see https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-jpaserver-model/ca/uhn/fhir/jpa/model/entity/StorageSettings.html#getNormalizedQuantitySearchLevel()). +When this feature is enabled, each row stored in HFJ_SPIDX_QUANTITY to also store a row in HFJ_SPIDX_QUANTITY_NRML in canonical UCUM units. +E.g. a weight recorded in an Observation as +``` +"valueQuantity" : { + "value" : 172, + "unit" : "lb_av", + "system" : "http://unitsofmeasure.org", + "code" : "[lb_av]" + }, + ``` +would match the search `Observation?valueQuantity=172`, +but would also match the search `Observation?valueQuantity=78|http://unitsofmeasure.org|kg`. +The row in HFJ_SPIDX_QUANTITY would contain the value 172 pounds, while the HFJ_SPIDX_QUANTITY_NRML table would hold the equivalent 78 kg value. +Only value searches that provide fully qualified units are eligible for normalized searches. +Sorting only uses the HFJ_SPIDX_QUANTITY table. + +## Columns + +Same as HFJ_SPIDX_QUANTITY above, except the SP_VALUE, SP_SYSTEM, and SP_UNITS columns hold the converted value in canonical units instead of the value extracted by the SearchParameter. +This table is only used for range queries with a unit which can be converted to canonical units. + + +# HFJ_SPIDX_STRING: String Search Parameters + +FHIR Search Parameters of type [*string*](https://www.hl7.org/fhir/search.html#string) produce rows in the `HFJ_SPIDX_STRING` table. +The default string search matches by prefix, ignoring case or accents. This uses the HASH_IDENTITY column and a LIKE prefix clause on the SP_VALUE_NORMALIZED columns. +The `:exact` string search matches exactly. This uses only the HASH_EXACT column. +Sorting is done via the HASH_IDENTITY and SP_VALUE_NORMALIZED columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_EXACTLong + A hash like HASH_IDENTITY that also includes the SP_VALUE_EXACT column. +
    SP_VALUE_NORMALIZEDString + An UPPERCASE string with accents removed. +
    SP_VALUE_EXACTString + The extracted string unchanged. +
    + +# HFJ_SPIDX_TOKEN: Token Search Parameters + +FHIR Search Parameters of type [*token*](https://www.hl7.org/fhir/search.html#token) extract values of type Coding, code, and others. +These produce rows in the `HFJ_SPIDX_TOKEN` table. +The default token search accepts three parameter formats: matching the code (e.g. `Observation?code=15074-8`), +matching both system and code (e.g. `Observation?code=http://loinc.org|15074-8`), +or matching a system with any code (e.g. `Observation?http://loinc.org|`). +All three are exact searches and use the hashes: HASH_VALUE, HASH_SYS_AND_VALUE, and HASH_SYS respectively. +Sorting is done via the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_VALUELong + A hash like HASH_IDENTITY that also includes the SP_VALUE column. +
    HASH_SYS_AND_VALUELong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM and SP_VALUE columns. +
    HASH_SYSLong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM column. +
    SP_SYSTEMString + The system of the code. +
    SP_VALUEString + This is the bare code value. +
    + + +# HFJ_SPIDX_URI: URI Search Parameters + +FHIR Search Parameters of type [*uri*](https://www.hl7.org/fhir/search.html#uri) produce rows in the `HFJ_SPIDX_URI` table. +The default uri search matches the complete uri. This uses the HASH_URI column for an exact match. +A uri search with the `:above` modifier will match any prefix. This also uses the HASH_URI column, but also tests hashes of every prefix of the query value. +A uri search with the `:below` modifier will match any extension. This query uses the HASH_IDENTITY and a LIKE prefix match of the SP_URI column. +Sorting is done via the HASH_IDENTITY and SP_URI columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_URILong + A hash like HASH_IDENTITY that also includes the SP_URI column. +
    SP_URIString + The uri string extracted by the SearchParameter. +
    + From 2e4f8fe4b61525dce577b5f57ec620653e7543ff Mon Sep 17 00:00:00 2001 From: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:42:47 -0500 Subject: [PATCH 32/86] fixing formatting. (#5458) Co-authored-by: peartree --- .../main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8bdc247c5d6..f4fbcbce057 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 @@ -746,7 +746,7 @@ E.g. a weight recorded in an Observation as "system" : "http://unitsofmeasure.org", "code" : "[lb_av]" }, - ``` +``` would match the search `Observation?valueQuantity=172`, but would also match the search `Observation?valueQuantity=78|http://unitsofmeasure.org|kg`. The row in HFJ_SPIDX_QUANTITY would contain the value 172 pounds, while the HFJ_SPIDX_QUANTITY_NRML table would hold the equivalent 78 kg value. From 6f84d17b13d6b974f1d973465a005bdb7bbbe4ee Mon Sep 17 00:00:00 2001 From: Martha Mitran Date: Mon, 20 Nov 2023 10:23:37 -0800 Subject: [PATCH 33/86] Expanding ValueSet using hierarchical CodeSystem fails with constraint violation exception (#5461) * Expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios with a constraint violation exception when codes (term concepts) were being persisted * Expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios with a constraint violation exception when codes (term concepts) were being persisted * Small changes after code review. Doing some variable/method renaming and correcting changelog Jira issue number. --- ...lizer_fails_capabilitystatement_dstu2.yaml | 2 +- ...valueset-with-hierarchical-codesystem.yaml | 6 + .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 91 ++++++++------- .../jpa/term/ValueSetExpansionR4Test.java | 9 +- .../ValueSetExpansionWithHierarchyR4Test.java | 109 ++++++++++++++++++ 5 files changed, 167 insertions(+), 50 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml index 771291f525e..2847a698b16 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml @@ -1,5 +1,5 @@ --- type: fix issue: 5431 -jira: SMILE-5306 +jira: SMILE-7589 title: "Previously, using VersionCanonicalizer to convert a CapabilityStatement from R5 to DSTU2 would fail. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml new file mode 100644 index 00000000000..704cadfb039 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5460 +jira: SMILE-7594 +title: "Previously, expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios +with a constraint violation exception when codes (term concepts) were being persisted. This is now fixed." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 447e3058c4d..73f95569ba6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -196,14 +196,9 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { public static final int DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS = 2; // doesn't seem to be much gain by using more threads than this value public static final int MAX_MASS_INDEXER_OBJECT_LOADING_THREADS = 6; - private static final int SINGLE_FETCH_SIZE = 1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermReadSvcImpl.class); private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions(); private static final TermCodeSystemVersionDetails NO_CURRENT_VERSION = new TermCodeSystemVersionDetails(-1L, null); - private static final String IDX_PROPERTIES = "myProperties"; - private static final String IDX_PROP_KEY = IDX_PROPERTIES + ".myKey"; - private static final String IDX_PROP_VALUE_STRING = IDX_PROPERTIES + ".myValueString"; - private static final String IDX_PROP_DISPLAY_STRING = IDX_PROPERTIES + ".myDisplayString"; private static final String OUR_PIPE_CHARACTER = "|"; private static final int SECONDS_IN_MINUTE = 60; private static final int INDEXED_ROOTS_LOGGING_COUNT = 50_000; @@ -540,6 +535,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { @Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, ExpansionFilter theFilter) { + Set addedCodes = new HashSet<>(); ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSetToExpand, "ValueSet to expand can not be null"); ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); @@ -560,9 +556,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { accumulator.addParameter().setName("count").setValue(new IntegerType(count)); } - myTxTemplate.executeWithoutResult(tx -> { - expandValueSetIntoAccumulator(theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true); - }); + myTxTemplate.executeWithoutResult(tx -> expandValueSetIntoAccumulator( + theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true, addedCodes)); if (accumulator.getTotalConcepts() != null) { accumulator.setTotal(accumulator.getTotalConcepts()); @@ -594,7 +589,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theAccumulator, ExpansionFilter theFilter, - boolean theAdd) { + boolean theAdd, + Set theAddedCodes) { Optional optionalTermValueSet; if (theValueSetToExpand.hasUrl()) { if (theValueSetToExpand.hasVersion()) { @@ -621,7 +617,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { "valueSetExpandedUsingInMemoryExpansion", getValueSetInfo(theValueSetToExpand)); theAccumulator.addMessage(msg); - doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); + doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter, theAddedCodes); return; } @@ -639,7 +635,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); theAccumulator.addMessage(msg); - doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); + doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter, theAddedCodes); return; } @@ -651,7 +647,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { .getLocalizer() .getMessage(TermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp); theAccumulator.addMessage(msg); - expandConcepts(theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect()); + expandConcepts( + theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, theAddedCodes, isOracleDialect()); } @Nonnull @@ -676,6 +673,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { TermValueSet theTermValueSet, ExpansionFilter theFilter, boolean theAdd, + Set theAddedCodes, boolean theOracle) { // NOTE: if you modifiy the logic here, look to `expandConceptsOracle` and see if your new code applies to its // copy pasted sibling @@ -708,10 +706,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } wasFilteredResult = true; } else { - // TODO JA HS: I'm pretty sure we are overfetching here. test says offset 3, count 4, but we are fetching - // index 3 -> 10 here, grabbing 7 concepts. - // Specifically this test - // testExpandInline_IncludePreExpandedValueSetByUri_FilterOnDisplay_LeftMatch_SelectRange if (theOracle) { conceptViews = myTermValueSetConceptViewOracleDao.findByTermValueSetId( offset, toIndex, theTermValueSet.getId()); @@ -802,26 +796,27 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { Long sourceConceptPid = pidToSourcePid.get(nextPid); String sourceConceptDirectParentPids = pidToSourceDirectParentPids.get(nextPid); - theAccumulator.includeConceptWithDesignations( - system, - code, - display, - designations, - sourceConceptPid, - sourceConceptDirectParentPids, - systemVersion); + if (theAddedCodes.add(system + OUR_PIPE_CHARACTER + code)) { + theAccumulator.includeConceptWithDesignations( + system, + code, + display, + designations, + sourceConceptPid, + sourceConceptDirectParentPids, + systemVersion); + if (wasFilteredResult) { + theAccumulator.incrementOrDecrementTotalConcepts(true, 1); + } + } } else { - boolean removed = theAccumulator.excludeConcept(system, code); - if (removed) { + if (theAddedCodes.remove(system + OUR_PIPE_CHARACTER + code)) { + theAccumulator.excludeConcept(system, code); theAccumulator.incrementOrDecrementTotalConcepts(false, 1); } } } - if (wasFilteredResult && theAdd) { - theAccumulator.incrementOrDecrementTotalConcepts(true, pidToConcept.size()); - } - logDesignationsExpanded("Finished expanding designations. ", theTermValueSet, designationsExpanded); logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded); } @@ -886,8 +881,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { + Set addedCodes = new HashSet<>(); doExpandValueSet( - theExpansionOptions, theValueSetToExpand, theValueSetCodeAccumulator, ExpansionFilter.NO_FILTER); + theExpansionOptions, + theValueSetToExpand, + theValueSetCodeAccumulator, + ExpansionFilter.NO_FILTER, + addedCodes); } /** @@ -898,8 +898,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, - @Nonnull ExpansionFilter theExpansionFilter) { - Set addedCodes = new HashSet<>(); + @Nonnull ExpansionFilter theExpansionFilter, + Set theAddedCodes) { StopWatch sw = new StopWatch(); String valueSetInfo = getValueSetInfo(theValueSetToExpand); @@ -908,7 +908,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { // Offset can't be combined with excludes Integer skipCountRemaining = theValueSetCodeAccumulator.getSkipCountRemaining(); if (skipCountRemaining != null && skipCountRemaining > 0) { - if (theValueSetToExpand.getCompose().getExclude().size() > 0) { + if (!theValueSetToExpand.getCompose().getExclude().isEmpty()) { String msg = myContext .getLocalizer() .getMessage(TermReadSvcImpl.class, "valueSetNotYetExpanded_OffsetNotAllowed", valueSetInfo); @@ -921,7 +921,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) { myTxTemplate.executeWithoutResult(tx -> expandValueSetHandleIncludeOrExclude( - theExpansionOptions, theValueSetCodeAccumulator, addedCodes, include, true, theExpansionFilter)); + theExpansionOptions, theValueSetCodeAccumulator, theAddedCodes, include, true, theExpansionFilter)); } // Handle excludes @@ -931,7 +931,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { myTxTemplate.executeWithoutResult(tx -> expandValueSetHandleIncludeOrExclude( theExpansionOptions, theValueSetCodeAccumulator, - addedCodes, + theAddedCodes, exclude, false, ExpansionFilter.NO_FILTER)); @@ -976,7 +976,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { String system = theIncludeOrExclude.getSystem(); boolean hasSystem = isNotBlank(system); - boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; + boolean hasValueSet = !theIncludeOrExclude.getValueSet().isEmpty(); if (hasSystem) { @@ -1004,7 +1004,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } else { - if (theIncludeOrExclude.getConcept().size() > 0 && theExpansionFilter.hasCode()) { + if (!theIncludeOrExclude.getConcept().isEmpty() && theExpansionFilter.hasCode()) { if (defaultString(theIncludeOrExclude.getSystem()).equals(theExpansionFilter.getSystem())) { if (theIncludeOrExclude.getConcept().stream() .noneMatch(t -> t.getCode().equals(theExpansionFilter.getCode()))) { @@ -1065,7 +1065,12 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } expandValueSetIntoAccumulator( - valueSet, theExpansionOptions, theValueSetCodeAccumulator, subExpansionFilter, theAdd); + valueSet, + theExpansionOptions, + theValueSetCodeAccumulator, + subExpansionFilter, + theAdd, + theAddedCodes); } } else { @@ -1855,12 +1860,11 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { if (!handled) { throwInvalidFilter( nextFilter, - " - Note that Hibernate Search is disabled on this server so not all ValueSet expansion funtionality is available."); + " - Note that Hibernate Search is disabled on this server so not all ValueSet expansion functionality is available."); } } - if (theInclude.getConcept().isEmpty()) { - + if (theInclude.getFilter().isEmpty() && theInclude.getConcept().isEmpty()) { Collection concepts = myConceptDao.fetchConceptsAndDesignationsByVersionPid(theVersion.getPid()); for (TermConcept next : concepts) { @@ -2392,7 +2396,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { sw); } catch (Exception e) { - ourLog.error("Failed to pre-expand ValueSet: " + e.getMessage(), e); + ourLog.error( + "Failed to pre-expand ValueSet with URL[{}]: {}", valueSetToExpand.getUrl(), e.getMessage(), e); txTemplate.executeWithoutResult(t -> { valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND); myTermValueSetDao.saveAndFlush(valueSetToExpand); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index bc22114e25f..2509c25d18a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -36,7 +36,6 @@ import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; @@ -73,9 +72,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R4); - @Mock - private IValueSetConceptAccumulator myValueSetCodeAccumulator; - @AfterEach public void afterEach() { SearchBuilder.setMaxPageSize50ForTest(false); @@ -280,8 +276,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { //Ensure that the subsequent expansion with offset returns the same slice we are anticipating. assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count)))); - Assertions.assertEquals(4, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); - assertEquals(11, expandedValueSet.getExpansion().getTotal()); + Assertions.assertEquals(count, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); + assertEquals(offset + count, expandedValueSet.getExpansion().getTotal()); + assertEquals(count, expandedValueSet.getExpansion().getContains().size()); // Make sure we used the pre-expanded version List selectQueries = myCaptureQueriesListener.getSelectQueries(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java new file mode 100644 index 00000000000..a03e78028b9 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java @@ -0,0 +1,109 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class ValueSetExpansionWithHierarchyR4Test extends BaseTermR4Test { + private static final String ourCodeSystemId = "CodeSystem-WithHierarchy", ourCodeSystemUrl = "http://example/" + ourCodeSystemId; + private static final String ourCodeA = "CodeA", ourCodeB = "CodeB", ourCodeC = "CodeC", ourCodeD = "CodeD"; + private static final String ourValueSetAUrl = "http://example/ValueSetA", ourValueSetBUrl = "http://example/ValueSetB", ourValueSetUrl = "http://example/ValueSet"; + private static final int ourChildConceptCount = 10; + + @BeforeAll + public static void setup() { + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); + } + @AfterAll + public static void tearDown() { + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + } + @BeforeEach + public void setupHierarchicalCodeSystemWithValueSets() { + myStorageSettings.setPreExpandValueSets(true); + + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept1 = codeSystem.addConcept().setCode(ourCodeA); + CodeSystem.ConceptDefinitionComponent concept2 = codeSystem.addConcept().setCode(ourCodeB); + for (int i = 0; i < ourChildConceptCount; i++) { + concept1.addConcept().setCode(concept1.getCode() + i); + concept2.addConcept().setCode(concept2.getCode() + i); + } + codeSystem.addConcept().setCode(ourCodeC); + codeSystem.addConcept().setCode(ourCodeD); + myCodeSystemDao.create(codeSystem, mySrd); + + ValueSet valueSetA = new ValueSet(); + valueSetA.setUrl(ourValueSetAUrl); + valueSetA.getCompose().addInclude().setSystem(ourCodeSystemUrl) + .addFilter().setProperty("concept").setOp(ValueSet.FilterOperator.ISA).setValue(ourCodeA); + myValueSetDao.create(valueSetA, mySrd); + + ValueSet valueSetB = new ValueSet(); + valueSetB.setUrl(ourValueSetBUrl); + valueSetA.getCompose().addInclude().setSystem(ourCodeSystemUrl) + .addFilter().setProperty("concept").setOp(ValueSet.FilterOperator.ISA).setValue(ourCodeB); + myValueSetDao.create(valueSetB, mySrd); + } + + static Stream parametersValueSets() { + ValueSet valueSet0 = new ValueSet(); + valueSet0.setUrl(ourValueSetUrl + "-WithIncludeChildValueSet"); + valueSet0.getCompose().addInclude().addValueSet(ourValueSetAUrl); + + ValueSet valueSet1 = new ValueSet(); + valueSet1.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystem"); + valueSet1.getCompose().addInclude().addValueSet(ourValueSetAUrl); + valueSet1.getCompose().addInclude().addValueSet(ourValueSetBUrl); + valueSet1.getCompose().addInclude().setSystem(ourCodeSystemUrl); + + ValueSet valueSet2 = new ValueSet(); + valueSet2.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystemConceptSet-NoIntersectionCodes"); + valueSet2.getCompose().addInclude().addValueSet(ourValueSetAUrl); + ValueSet.ConceptSetComponent conceptSetWithCodeSystem = valueSet2.getCompose().addInclude().setSystem(ourCodeSystemUrl); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeC); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeD); + + ValueSet valueSet3 = new ValueSet(); + valueSet3.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystemConceptSet-WithIntersectionCodes"); + valueSet3.getCompose().addInclude().addValueSet(ourValueSetAUrl); + conceptSetWithCodeSystem = valueSet3.getCompose().addInclude().setSystem(ourCodeSystemUrl); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeA + "1"); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeA + "2"); + + return Stream.of( + arguments(valueSet0, ourChildConceptCount), + arguments(valueSet1, 4 + ourChildConceptCount * 2), + arguments(valueSet2, 2 + ourChildConceptCount), + arguments(valueSet3, ourChildConceptCount) + ); + } + + @ParameterizedTest + @MethodSource(value = "parametersValueSets") + public void testExpandValueSet_whenUsingHierarchicalCodeSystem_willExpandSuccessfully(ValueSet theValueSet, int theExpectedConceptExpensionCount) { + myValueSetDao.create(theValueSet, mySrd); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Optional optionalTermValueSet = runInTransaction(() -> myTermValueSetDao.findTermValueSetByUrlAndNullVersion(theValueSet.getUrl())); + assertTrue(optionalTermValueSet.isPresent()); + TermValueSet expandedTermValueSet = optionalTermValueSet.get(); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, expandedTermValueSet.getExpansionStatus()); + assertEquals(theExpectedConceptExpensionCount, expandedTermValueSet.getTotalConcepts()); + } +} From 6abfed603e5ad755633a1d737f66618923c6d925 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Mon, 20 Nov 2023 17:39:36 -0500 Subject: [PATCH 34/86] Use streams to avoid multiple queries during batch job id chunking. (#5444) Use stream for chunking instead of repeated sorted query pages. --- .../java/ca/uhn/fhir/util/StreamUtil.java | 57 +++++ .../java/ca/uhn/fhir/util/StreamUtilTest.java | 65 ++++++ .../changelog/7_0_0/5444-batch-chunking.yaml | 4 + .../fhir/jpa/config/Batch2SupportConfig.java | 10 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 25 +++ .../fhir/jpa/dao/data/IResourceTableDao.java | 20 +- .../fhir/jpa/reindex/Batch2DaoSvcImpl.java | 194 +++++++++--------- .../jpa/search/builder/SearchBuilder.java | 2 +- .../jpa/search/cache/ISearchCacheSvc.java | 6 - .../term/TermConceptClientMappingSvcImpl.java | 19 ++ .../mdm/svc/GoldenResourceSearchSvcImpl.java | 50 ++--- .../jpa/dao/r4/FhirResourceDaoR4Test.java | 35 +++- .../jpa/reindex/Batch2DaoSvcImplTest.java | 58 ++---- .../reindex/ResourceReindexSvcImplTest.java | 39 ++-- .../jpa/test/config/ConnectionWrapper.java | 5 +- .../batch2/jobs/step/IIdChunkProducer.java | 20 +- .../PartitionedUrlListIdChunkProducer.java | 33 ++- .../batch2/jobs/step/ResourceIdListStep.java | 60 ++---- .../batch2/jobs/step/LoadIdsStepTest.java | 9 +- .../jobs/step/ResourceIdListStepTest.java | 30 ++- .../fhir/mdm/batch2/MdmIdChunkProducer.java | 18 +- .../fhir/jpa/api/dao/IFhirResourceDao.java | 17 ++ .../api/pid/AutoClosingStreamTemplate.java | 45 ++++ .../fhir/jpa/api/pid/BaseResourcePidList.java | 10 +- .../fhir/jpa/api/pid/IResourcePidStream.java | 45 ++++ .../jpa/api/pid/ListWrappingPidStream.java | 47 +++++ .../jpa/api/pid/MixedResourcePidList.java | 2 +- .../uhn/fhir/jpa/api/pid/StreamTemplate.java | 60 ++++++ .../TransactionWrappingStreamTemplate.java | 52 +++++ .../fhir/jpa/api/pid/TypedResourceStream.java | 47 +++++ .../uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java | 8 + .../jpa/api/svc/IGoldenResourceSearchSvc.java | 10 +- .../ca/uhn/fhir/jpa/dao/ISearchBuilder.java | 2 +- .../jpa/dao/tx/HapiTransactionService.java | 5 +- .../jpa/dao/tx/IHapiTransactionService.java | 5 +- .../pid/AutoClosingStreamTemplateTest.java | 65 ++++++ 36 files changed, 851 insertions(+), 328 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java create mode 100644 hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java create mode 100644 hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java new file mode 100644 index 00000000000..97a8ff4ed43 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java @@ -0,0 +1,57 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 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.util; + +import com.google.common.collect.Iterators; +import com.google.common.collect.UnmodifiableIterator; + +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class StreamUtil { + /** Static util class */ + private StreamUtil() {} + + /** + * Chunk the stream into Lists of size theChunkSize. + * The last chunk will be smaller unless the stream size is evenly divisible. + * Closes the underlying stream when done. + * + * @param theStream the input stream + * @param theChunkSize the chunk size. + * @return a stream of chunks + */ + public static Stream> partition(Stream theStream, int theChunkSize) { + Spliterator spliterator = theStream.spliterator(); + Iterator iterator = Spliterators.iterator(spliterator); + UnmodifiableIterator> partition = Iterators.partition(iterator, theChunkSize); + + // we could be fancier here and support parallel, and sizes; but serial-only is fine for now. + Spliterator> partitionedSpliterator = Spliterators.spliteratorUnknownSize(partition, 0); + Stream> result = StreamSupport.stream(partitionedSpliterator, false); + + // we lose close() via the Iterator. Add it back. + return result.onClose(theStream::close); + } +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java new file mode 100644 index 00000000000..0a805af02d2 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class StreamUtilTest { + + @ParameterizedTest + @MethodSource("streamPartitionTestCases") + void testStreamPartitionBy4(String theCase, List theInput, List> theOutput) { + List> result = StreamUtil.partition(theInput.stream(), 4).toList(); + + assertEquals(theOutput, result, theCase); + } + + static Object[][] streamPartitionTestCases() { + return new Object[][]{ + { + "empty list produces empty stream", + List.of(), + List.of() + }, + { + "short list produces single chunk", + List.of(1, 2, 3), + List.of(List.of(1, 2, 3)) + }, + { + "longer list produces several chunks", + List.of(1, 2, 3, 1, 2, 3, 1, 2, 3), + List.of(List.of(1, 2, 3, 1), List.of(2, 3, 1, 2), List.of(3)) + }, + { + "even size produces even chunks", + List.of(1, 2, 3,4,5,6,7,8), + List.of(List.of(1, 2, 3,4), List.of(5,6,7,8)) + }, + }; + } + + @Test + void testStreamPartitionClosesOriginalStream() { + // given + AtomicBoolean closed = new AtomicBoolean(false); + Stream baseStream = Stream.of(1, 2, 3).onClose(()->closed.set(true)); + + // when + StreamUtil.partition(baseStream, 2).close(); + + // then + assertThat("partition closed underlying stream", closed.get()); + } + + +} diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml new file mode 100644 index 00000000000..8e188e5785d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5444 +title: "The reindexing and mdm-clear batch jobs now stream results internally for more reliable operation." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java index 0ea5cae5175..16cda3e728c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java @@ -47,15 +47,9 @@ public class Batch2SupportConfig { MatchUrlService theMatchUrlService, DaoRegistry theDaoRegistry, FhirContext theFhirContext, - IHapiTransactionService theTransactionService, - JpaStorageSettings theJpaStorageSettings) { + IHapiTransactionService theTransactionService) { return new Batch2DaoSvcImpl( - theResourceTableDao, - theMatchUrlService, - theDaoRegistry, - theFhirContext, - theTransactionService, - theJpaStorageSettings); + theResourceTableDao, theMatchUrlService, theDaoRegistry, theFhirContext, theTransactionService); } @Bean 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 ddde3bc3231..ba1a21a0eea 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 @@ -117,6 +117,8 @@ import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ValidationOptions; import ca.uhn.fhir.validation.ValidationResult; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Streams; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseMetaType; @@ -150,6 +152,7 @@ import java.util.UUID; import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.PostConstruct; @@ -2067,6 +2070,28 @@ public abstract class BaseHapiFhirResourceDao extends B }); } + public > Stream searchForIdStream( + SearchParameterMap theParams, + RequestDetails theRequest, + @Nullable IBaseResource theConditionalOperationTargetOrNull) { + // the Stream is useless outside the bound connection time, so require our caller to have a session. + HapiTransactionService.requireTransaction(); + + RequestPartitionId requestPartitionId = + myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType( + theRequest, myResourceName, theParams, theConditionalOperationTargetOrNull); + + ISearchBuilder builder = mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); + + String uuid = UUID.randomUUID().toString(); + + SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequest, uuid); + IResultIterator iter = + builder.createQuery(theParams, searchRuntimeDetails, theRequest, requestPartitionId); + // Adapt IResultIterator to stream, and connect the close handler. + return Streams.stream(iter).onClose(() -> IOUtils.closeQuietly(iter)); + } + protected MT toMetaDt(Class theType, Collection tagDefinitions) { MT retVal = ReflectionUtil.newInstance(theType); for (TagDefinition next : tagDefinitions) { 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 6bb08cbdc5e..c1263047940 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 @@ -35,6 +35,7 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; @Transactional(propagation = Propagation.MANDATORY) public interface IResourceTableDao @@ -65,13 +66,10 @@ public interface IResourceTableDao Slice findIdsOfResourcesWithinUpdatedRangeOrderedFromOldest( Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); - /** - * @return List of arrays containing [PID, resourceType, lastUpdated] - */ @Query( "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC") - Slice findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( - Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( + @Param("low") Date theLow, @Param("high") Date theHigh); /** * @return List of arrays containing [PID, resourceType, lastUpdated] @@ -84,6 +82,13 @@ public interface IResourceTableDao @Param("high") Date theHigh, @Param("partition_ids") List theRequestPartitionIds); + @Query( + "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue IN (:partition_ids) ORDER BY t.myUpdated ASC") + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( + @Param("low") Date theLow, + @Param("high") Date theHigh, + @Param("partition_ids") List theRequestPartitionIds); + /** * @return List of arrays containing [PID, resourceType, lastUpdated] */ @@ -92,6 +97,11 @@ public interface IResourceTableDao Slice findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); + @Query( + "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC") + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( + @Param("low") Date theLow, @Param("high") Date theHigh); + // TODO in the future, consider sorting by pid as well so batch jobs process in the same order across restarts @Query( "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue = :partition_id ORDER BY t.myUpdated ASC") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java index 1a8e3e63883..26d7f77625a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.pid.EmptyResourcePidList; -import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; -import ca.uhn.fhir.jpa.api.pid.MixedResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; +import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.TypedResourceStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; @@ -37,18 +37,15 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; +import ca.uhn.fhir.util.DateRangeUtil; +import org.apache.commons.lang3.Validate; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; +import java.util.function.Supplier; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -65,8 +62,6 @@ public class Batch2DaoSvcImpl implements IBatch2DaoSvc { private final IHapiTransactionService myTransactionService; - private final JpaStorageSettings myJpaStorageSettings; - @Override public boolean isAllResourceTypeSupported() { return true; @@ -77,113 +72,110 @@ public class Batch2DaoSvcImpl implements IBatch2DaoSvc { MatchUrlService theMatchUrlService, DaoRegistry theDaoRegistry, FhirContext theFhirContext, - IHapiTransactionService theTransactionService, - JpaStorageSettings theJpaStorageSettings) { + IHapiTransactionService theTransactionService) { myResourceTableDao = theResourceTableDao; myMatchUrlService = theMatchUrlService; myDaoRegistry = theDaoRegistry; myFhirContext = theFhirContext; myTransactionService = theTransactionService; - myJpaStorageSettings = theJpaStorageSettings; } @Override - public IResourcePidList fetchResourceIdsPage( - Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, @Nullable String theUrl) { - return myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(() -> { - if (theUrl == null) { - return fetchResourceIdsPageNoUrl(theStart, theEnd, theRequestPartitionId); - } else { - return fetchResourceIdsPageWithUrl(theEnd, theUrl, theRequestPartitionId); - } - }); + public IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId, String theUrl) { + if (theUrl == null) { + return makeStreamResult( + theRequestPartitionId, () -> streamResourceIdsNoUrl(theStart, theEnd, theRequestPartitionId)); + } else { + return makeStreamResult( + theRequestPartitionId, + () -> streamResourceIdsWithUrl(theStart, theEnd, theUrl, theRequestPartitionId)); + } + } + + private Stream streamResourceIdsWithUrl( + Date theStart, Date theEnd, String theUrl, RequestPartitionId theRequestPartitionId) { + validateUrl(theUrl); + + SearchParameterMap searchParamMap = parseQuery(theUrl); + searchParamMap.setLastUpdated(DateRangeUtil.narrowDateRange(searchParamMap.getLastUpdated(), theStart, theEnd)); + + String resourceType = theUrl.substring(0, theUrl.indexOf('?')); + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); + + SystemRequestDetails request = new SystemRequestDetails().setRequestPartitionId(theRequestPartitionId); + + return dao.searchForIdStream(searchParamMap, request, null).map(pid -> new TypedResourcePid(resourceType, pid)); + } + + private static TypedResourcePid typedPidFromQueryArray(Object[] thePidTypeDateArray) { + String resourceType = (String) thePidTypeDateArray[1]; + Long pid = (Long) thePidTypeDateArray[0]; + return new TypedResourcePid(resourceType, JpaPid.fromId(pid)); } @Nonnull - private HomogeneousResourcePidList fetchResourceIdsPageWithUrl( - Date theEnd, @Nonnull String theUrl, @Nullable RequestPartitionId theRequestPartitionId) { + private TypedResourceStream makeStreamResult( + RequestPartitionId theRequestPartitionId, Supplier> streamSupplier) { + + IHapiTransactionService.IExecutionBuilder txSettings = + myTransactionService.withSystemRequest().withRequestPartitionId(theRequestPartitionId); + + StreamTemplate streamTemplate = + StreamTemplate.fromSupplier(streamSupplier).withTransactionAdvice(txSettings); + + return new TypedResourceStream(theRequestPartitionId, streamTemplate); + } + + @Nonnull + private Stream streamResourceIdsNoUrl( + Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId) { + Stream rowStream; + if (theRequestPartitionId == null || theRequestPartitionId.isAllPartitions()) { + ourLog.debug("Search for resources - all partitions"); + rowStream = myResourceTableDao.streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( + theStart, theEnd); + } else if (theRequestPartitionId.isDefaultPartition()) { + ourLog.debug("Search for resources - default partition"); + rowStream = + myResourceTableDao + .streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( + theStart, theEnd); + } else { + ourLog.debug("Search for resources - partition {}", theRequestPartitionId); + rowStream = + myResourceTableDao + .streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( + theStart, theEnd, theRequestPartitionId.getPartitionIds()); + } + + return rowStream.map(Batch2DaoSvcImpl::typedPidFromQueryArray); + } + + @Deprecated(since = "6.11", forRemoval = true) // delete once the default method in the interface is gone. + @Override + public IResourcePidList fetchResourceIdsPage( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, @Nullable String theUrl) { + Validate.isTrue(false, "Unimplemented"); + return null; + } + + private static void validateUrl(@Nonnull String theUrl) { if (!theUrl.contains("?")) { throw new InternalErrorException(Msg.code(2422) + "this should never happen: URL is missing a '?'"); } - - final Integer internalSynchronousSearchSize = myJpaStorageSettings.getInternalSynchronousSearchSize(); - - if (internalSynchronousSearchSize == null || internalSynchronousSearchSize <= 0) { - throw new InternalErrorException(Msg.code(2423) - + "this should never happen: internalSynchronousSearchSize is null or less than or equal to 0"); - } - - List currentIds = fetchResourceIdsPageWithUrl(0, theUrl, theRequestPartitionId); - ourLog.debug("FIRST currentIds: {}", currentIds.size()); - - final List allIds = new ArrayList<>(currentIds); - - while (internalSynchronousSearchSize < currentIds.size()) { - // Ensure the offset is set to the last ID in the cumulative List, otherwise, we'll be stuck in an infinite - // loop here: - currentIds = fetchResourceIdsPageWithUrl(allIds.size(), theUrl, theRequestPartitionId); - ourLog.debug("NEXT currentIds: {}", currentIds.size()); - - allIds.addAll(currentIds); - } - - final String resourceType = theUrl.substring(0, theUrl.indexOf('?')); - - return new HomogeneousResourcePidList(resourceType, allIds, theEnd, theRequestPartitionId); } - private List fetchResourceIdsPageWithUrl( - int theOffset, String theUrl, RequestPartitionId theRequestPartitionId) { + @Nonnull + private SearchParameterMap parseQuery(String theUrl) { String resourceType = theUrl.substring(0, theUrl.indexOf('?')); RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(resourceType); SearchParameterMap searchParamMap = myMatchUrlService.translateMatchUrl(theUrl, def); - searchParamMap.setSort(new SortSpec(Constants.PARAM_ID, SortOrderEnum.ASC)); - searchParamMap.setOffset(theOffset); - searchParamMap.setLoadSynchronousUpTo(myJpaStorageSettings.getInternalSynchronousSearchSize() + 1); - - IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); - SystemRequestDetails request = new SystemRequestDetails(); - request.setRequestPartitionId(theRequestPartitionId); - - return dao.searchForIds(searchParamMap, request); - } - - @Nonnull - private IResourcePidList fetchResourceIdsPageNoUrl( - Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId) { - final Pageable page = Pageable.unpaged(); - Slice slice; - if (theRequestPartitionId == null || theRequestPartitionId.isAllPartitions()) { - slice = myResourceTableDao.findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( - page, theStart, theEnd); - } else if (theRequestPartitionId.isDefaultPartition()) { - slice = - myResourceTableDao - .findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( - page, theStart, theEnd); - } else { - slice = - myResourceTableDao - .findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( - page, theStart, theEnd, theRequestPartitionId.getPartitionIds()); - } - - List content = slice.getContent(); - if (content.isEmpty()) { - return new EmptyResourcePidList(); - } - - List ids = - content.stream().map(t -> JpaPid.fromId((Long) t[0])).collect(Collectors.toList()); - - List types = content.stream().map(t -> (String) t[1]).collect(Collectors.toList()); - - Date lastDate = (Date) content.get(content.size() - 1)[2]; - - return new MixedResourcePidList(types, ids, lastDate, theRequestPartitionId); + // this matches idx_res_type_del_updated + searchParamMap.setSort(new SortSpec(Constants.PARAM_LASTUPDATED).setChain(new SortSpec(Constants.PARAM_PID))); + // TODO this limits us to 2G resources. + searchParamMap.setLoadSynchronousUpTo(Integer.MAX_VALUE); + return searchParamMap; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index a268f5f0d84..88f5f75a87e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -339,7 +339,7 @@ public class SearchBuilder implements ISearchBuilder { @SuppressWarnings("ConstantConditions") @Override - public IResultIterator createQuery( + public IResultIterator createQuery( SearchParameterMap theParams, SearchRuntimeDetails theSearchRuntimeDetails, RequestDetails theRequest, diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java index 8c9ab6f0ec1..b417ce9dd5e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java @@ -23,7 +23,6 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.entity.Search; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.Optional; public interface ISearchCacheSvc { @@ -88,9 +87,4 @@ public interface ISearchCacheSvc { * and deleting them. */ void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline); - - @Deprecated(since = "6.10", forRemoval = true) // wipmb delete once cdr merges - default void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId) { - pollForStaleSearchesAndDeleteThem(theRequestPartitionId, Instant.now().plus(1, ChronoUnit.MINUTES)); - } } 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 afee40feeab..de3a7809bb1 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 @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 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.term; import ca.uhn.fhir.context.FhirContext; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java index c7e967075d6..62c259bca4b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java @@ -24,9 +24,12 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; +import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.TypedResourceStream; import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.MdmConstants; @@ -34,7 +37,6 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.util.DateRangeUtil; @@ -42,7 +44,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import java.util.Date; -import java.util.List; +import java.util.function.Supplier; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -56,24 +59,31 @@ public class GoldenResourceSearchSvcImpl implements IGoldenResourceSearchSvc { @Autowired private FhirContext myFhirContext; + @Autowired + private IHapiTransactionService myTransactionService; + @Override @Transactional - public IResourcePidList fetchGoldenResourceIdsPage( + public IResourcePidStream fetchGoldenResourceIdStream( Date theStart, Date theEnd, - @Nonnull Integer thePageSize, @Nullable RequestPartitionId theRequestPartitionId, @Nonnull String theResourceType) { - return fetchResourceIdsPageWithResourceType( - theStart, theEnd, thePageSize, theResourceType, theRequestPartitionId); + + IHapiTransactionService.IExecutionBuilder txSettings = + myTransactionService.withSystemRequest().withRequestPartitionId(theRequestPartitionId); + + Supplier> streamSupplier = + () -> fetchResourceIdsPageWithResourceType(theStart, theEnd, theResourceType, theRequestPartitionId); + + StreamTemplate streamTemplate = + StreamTemplate.fromSupplier(streamSupplier).withTransactionAdvice(txSettings); + + return new TypedResourceStream(theRequestPartitionId, streamTemplate); } - private IResourcePidList fetchResourceIdsPageWithResourceType( - Date theStart, - Date theEnd, - int thePageSize, - String theResourceType, - RequestPartitionId theRequestPartitionId) { + private Stream fetchResourceIdsPageWithResourceType( + Date theStart, Date theEnd, String theResourceType, RequestPartitionId theRequestPartitionId) { RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(theResourceType); @@ -89,15 +99,9 @@ public class GoldenResourceSearchSvcImpl implements IGoldenResourceSearchSvc { searchParamMap.add(Constants.PARAM_TAG, goldenRecordStatusToken); IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceType); - SystemRequestDetails request = new SystemRequestDetails(); - request.setRequestPartitionId(theRequestPartitionId); - List ids = dao.searchForIds(searchParamMap, request); + SystemRequestDetails request = new SystemRequestDetails().setRequestPartitionId(theRequestPartitionId); - Date lastDate = null; - if (ids.size() > 0) { - lastDate = dao.readByPid(ids.get(ids.size() - 1)).getMeta().getLastUpdated(); - } - - return new HomogeneousResourcePidList(theResourceType, ids, lastDate, theRequestPartitionId); + return dao.searchForIdStream(searchParamMap, request, null) + .map(pid -> new TypedResourcePid(theResourceType, pid)); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index ab11fcd27f1..569ff1aad7c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -5,9 +5,11 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseStorageDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; @@ -33,6 +35,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; @@ -119,24 +122,27 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static ca.uhn.fhir.batch2.jobs.termcodesystem.TermCodeSystemJobConfig.TERM_CODE_SYSTEM_VERSION_DELETE_JOB_NAME; import static ca.uhn.fhir.rest.api.Constants.PARAM_HAS; @@ -165,6 +171,9 @@ import static org.junit.jupiter.api.Assertions.fail; @SuppressWarnings({"unchecked", "deprecation", "Duplicates"}) public class FhirResourceDaoR4Test extends BaseJpaR4Test { + @Autowired + IHapiTransactionService myHapiTransactionService; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4Test.class); @AfterEach @@ -4260,6 +4269,30 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { assertThat(actualNameList, contains(namesInAlpha)); } + + @Test + void testSearchForStream_carriesTxContext() { + // given + Set createdIds = IntStream.range(1, 5) + .mapToObj(i -> createObservation().getIdPart()) + .collect(Collectors.toSet()); + + SystemRequestDetails request = new SystemRequestDetails(); + + // call within a tx, but carry the tx definition in the StreamTemplate + StreamTemplate> streamTemplate = + StreamTemplate.fromSupplier(() -> myObservationDao.searchForIdStream(new SearchParameterMap(), request, null)) + .withTransactionAdvice(newTxTemplate()); + + + // does the stream work? + Set ids = streamTemplate.call(stream-> + stream.map(typedId->typedId.getId().toString()) + .collect(Collectors.toSet())); + + assertEquals(ids, createdIds); + } + public static void assertConflictException(String theResourceType, ResourceVersionConflictException e) { assertThat(e.getMessage(), matchesPattern( Msg.code(550) + Msg.code(515) + "Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource " + theResourceType + "/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+")); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java index d4f5dbf07d2..e20101c3ef1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java @@ -1,14 +1,13 @@ package ca.uhn.fhir.jpa.reindex; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.api.dao.DaoRegistry; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.BeforeEach; @@ -24,12 +23,10 @@ import java.time.ZoneId; import java.util.Date; import java.util.List; import java.util.stream.IntStream; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; class Batch2DaoSvcImplTest extends BaseJpaR4Test { @@ -37,49 +34,46 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { private static final Date TOMORROW = toDate(LocalDate.now().plusDays(1)); private static final String URL_PATIENT_EXPUNGE_TRUE = "Patient?_expunge=true"; private static final String PATIENT = "Patient"; - private static final int INTERNAL_SYNCHRONOUS_SEARCH_SIZE = 10; - @Autowired - private JpaStorageSettings myJpaStorageSettings; @Autowired private MatchUrlService myMatchUrlService; @Autowired private IHapiTransactionService myIHapiTransactionService ; - private DaoRegistry mySpiedDaoRegistry; - private IBatch2DaoSvc mySubject; + @BeforeEach void beforeEach() { - myJpaStorageSettings.setInternalSynchronousSearchSize(INTERNAL_SYNCHRONOUS_SEARCH_SIZE); - mySpiedDaoRegistry = spy(myDaoRegistry); - - mySubject = new Batch2DaoSvcImpl(myResourceTableDao, myMatchUrlService, mySpiedDaoRegistry, myFhirContext, myIHapiTransactionService, myJpaStorageSettings); + mySubject = new Batch2DaoSvcImpl(myResourceTableDao, myMatchUrlService, myDaoRegistry, myFhirContext, myIHapiTransactionService); } // TODO: LD this test won't work with the nonUrl variant yet: error: No existing transaction found for transaction marked with propagation 'mandatory' @Test void fetchResourcesByUrlEmptyUrl() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "")); + final InternalErrorException exception = + assertThrows( + InternalErrorException.class, + () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "") + .visitStream(Stream::toList)); assertEquals("HAPI-2422: this should never happen: URL is missing a '?'", exception.getMessage()); } @Test void fetchResourcesByUrlSingleQuestionMark() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "?")); + final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "?").visitStream(Stream::toList)); - assertEquals("HAPI-2223: theResourceName must not be blank", exception.getMessage()); + assertEquals("theResourceName must not be blank", exception.getMessage()); } @Test void fetchResourcesByUrlNonsensicalResource() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "Banana?_expunge=true")); + final DataFormatException exception = assertThrows(DataFormatException.class, () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "Banana?_expunge=true").visitStream(Stream::toList)); - assertEquals("HAPI-2223: HAPI-1684: Unknown resource name \"Banana\" (this name is not known in FHIR version \"R4\")", exception.getMessage()); + assertEquals("HAPI-1684: Unknown resource name \"Banana\" (this name is not known in FHIR version \"R4\")", exception.getMessage()); } @ParameterizedTest @@ -89,16 +83,12 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { .mapToObj(num -> createPatient()) .toList(); - final IResourcePidList resourcePidList = mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), URL_PATIENT_EXPUNGE_TRUE); + final IResourcePidStream resourcePidList = mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), URL_PATIENT_EXPUNGE_TRUE); final List actualPatientIds = - resourcePidList.getTypedResourcePids() - .stream() - .map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) - .toList(); + resourcePidList.visitStream(s-> s.map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) + .toList()); assertIdsEqual(patientIds, actualPatientIds); - - verify(mySpiedDaoRegistry, times(getExpectedNumOfInvocations(expectedNumResults))).getResourceDao(PATIENT); } @ParameterizedTest @@ -109,22 +99,14 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { .mapToObj(num -> createPatient()) .toList(); - final IResourcePidList resourcePidList = mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, pageSizeWellBelowThreshold, RequestPartitionId.defaultPartition(), null); + final IResourcePidStream resourcePidList = mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), null); final List actualPatientIds = - resourcePidList.getTypedResourcePids() - .stream() - .map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) - .toList(); + resourcePidList.visitStream(s-> s.map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) + .toList()); assertIdsEqual(patientIds, actualPatientIds); } - private int getExpectedNumOfInvocations(int expectedNumResults) { - final int maxResultsPerQuery = INTERNAL_SYNCHRONOUS_SEARCH_SIZE + 1; - final int division = expectedNumResults / maxResultsPerQuery; - return division + 1; - } - private static void assertIdsEqual(List expectedResourceIds, List actualResourceIds) { assertEquals(expectedResourceIds.size(), actualResourceIds.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java index 56c09865ea0..2fcf5b19b1e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java @@ -1,10 +1,9 @@ package ca.uhn.fhir.jpa.reindex; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; -import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -12,15 +11,13 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; +import java.util.stream.Stream; -import static ca.uhn.fhir.batch2.jobs.step.ResourceIdListStep.DEFAULT_PAGE_SIZE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@SuppressWarnings("unchecked") @TestMethodOrder(value = MethodOrderer.MethodName.class) public class ResourceReindexSvcImplTest extends BaseJpaR4Test { @@ -55,14 +52,12 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, null); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, null); // Verify - - assertEquals(3, page.size()); - assertThat(page.getTypedResourcePids(), contains(new TypedResourcePid("Patient", id0), new TypedResourcePid("Patient", id1), new TypedResourcePid("Observation", id2))); - assertTrue(page.getLastDate().after(beforeLastInRange)); - assertTrue(page.getLastDate().before(end)); + List typedPids = queryStream.visitStream(Stream::toList); + assertEquals(3, typedPids.size()); + assertThat(typedPids, contains(new TypedResourcePid("Patient", id0), new TypedResourcePid("Patient", id1), new TypedResourcePid("Observation", id2))); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -85,13 +80,12 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, null); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, null); // Verify + List typedPids = queryStream.visitStream(Stream::toList); - assertTrue(page.isEmpty()); - assertEquals(0, page.size()); - assertNull(page.getLastDate()); + assertTrue(typedPids.isEmpty()); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -133,19 +127,16 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, "Patient?active=false"); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, "Patient?active=false"); // Verify + List typedResourcePids = queryStream.visitStream(Stream::toList); - assertEquals(4, page.size()); - List typedResourcePids = page.getTypedResourcePids(); - assertThat(page.getTypedResourcePids(), - contains(new TypedResourcePid("Patient", patientId0), + assertEquals(2, typedResourcePids.size()); + assertThat(typedResourcePids, + contains( new TypedResourcePid("Patient", patientId1), - new TypedResourcePid("Patient", patientId2), - new TypedResourcePid("Patient", patientId3))); - assertTrue(page.getLastDate().after(beforeLastInRange)); - assertTrue(page.getLastDate().before(end) || page.getLastDate().equals(end)); + new TypedResourcePid("Patient", patientId2))); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java index ede47637936..e211fd0f89d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java @@ -38,13 +38,15 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; +@SuppressWarnings("SqlSourceToSinkFlow") public class ConnectionWrapper implements Connection { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ConnectionWrapper.class); - private Connection myWrap; + private final Connection myWrap; public ConnectionWrapper(Connection theConnection) { + ourLog.trace("new connection - {}", theConnection); myWrap = theConnection; } @@ -60,6 +62,7 @@ public class ConnectionWrapper implements Connection { @Override public void close() throws SQLException { + ourLog.trace("close connection - {}", myWrap); myWrap.close(); } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java index ac16061c87c..730996fa597 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java @@ -21,10 +21,9 @@ package ca.uhn.fhir.batch2.jobs.step; import ca.uhn.fhir.batch2.jobs.chunk.ChunkRangeJson; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import java.util.Date; -import javax.annotation.Nonnull; import javax.annotation.Nullable; /** @@ -35,19 +34,6 @@ import javax.annotation.Nullable; * @param This parameter defines constraints on the types of pids we are pulling (e.g. resource type, url, etc). */ public interface IIdChunkProducer { - /** - * Actually fetch the resource pids - * @param theNextStart pids are pulled with lastUpdated >= this date - * @param theEnd pids are pulled with lastUpdate <= this date - * @param thePageSize the number of pids to query at a time - * @param theRequestPartitionId partition for operation if rtequired - * @param theData defines the query we are using - * @return a list of Resource pids - */ - IResourcePidList fetchResourceIdsPage( - Date theNextStart, - Date theEnd, - @Nonnull Integer thePageSize, - @Nullable RequestPartitionId theRequestPartitionId, - IT theData); + IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, IT theData); } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java index 9e23825ab12..23f5cb4e202 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java @@ -22,15 +22,16 @@ package ca.uhn.fhir.batch2.jobs.step; import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.util.Logs; import org.slf4j.Logger; import java.util.Date; -import javax.annotation.Nonnull; import javax.annotation.Nullable; +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + public class PartitionedUrlListIdChunkProducer implements IIdChunkProducer { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); private final IBatch2DaoSvc myBatch2DaoSvc; @@ -40,29 +41,27 @@ public class PartitionedUrlListIdChunkProducer implements IIdChunkProducer implements IJobStepWorker { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); @@ -67,49 +66,32 @@ public class ResourceIdListStep { + AtomicInteger totalIdsFound = new AtomicInteger(); + AtomicInteger chunkCount = new AtomicInteger(); - final Set idBuffer = nextChunk.getTypedResourcePids().stream() - .map(TypedPidJson::new) - .collect(Collectors.toCollection(LinkedHashSet::new)); - - final UnmodifiableIterator> partition = - Iterators.partition(idBuffer.iterator(), maxBatchId); - - while (partition.hasNext()) { - final List submissionIds = partition.next(); - - totalIdsFound += submissionIds.size(); - chunkCount++; - submitWorkChunk(submissionIds, nextChunk.getRequestPartitionId(), theDataSink); - } + Stream jsonStream = typedResourcePidStream.map(TypedPidJson::new); + // chunk by size maxBatchId and submit the batches + partition(jsonStream, chunkSize).forEach(idBatch -> { + totalIdsFound.addAndGet(idBatch.size()); + chunkCount.getAndIncrement(); + submitWorkChunk(idBatch, searchResult.getRequestPartitionId(), theDataSink); + }); ourLog.info("Submitted {} chunks with {} resource IDs", chunkCount, totalIdsFound); - } + }); + return RunOutcome.SUCCESS; } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java index ac06d4ba3ce..de8304328fa 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java @@ -8,6 +8,8 @@ import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; @@ -25,7 +27,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import static ca.uhn.fhir.batch2.jobs.step.ResourceIdListStep.DEFAULT_PAGE_SIZE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -68,7 +69,7 @@ public class LoadIdsStepTest { // First Execution - when(myBatch2DaoSvc.fetchResourceIdsPage(eq(DATE_1), eq(DATE_END), eq(DEFAULT_PAGE_SIZE), isNull(), isNull())) + when(myBatch2DaoSvc.fetchResourceIdStream(eq(DATE_1), eq(DATE_END), isNull(), isNull())) .thenReturn(createIdChunk(0L, 20000L, DATE_2)); mySvc.run(details, mySink); @@ -96,14 +97,14 @@ public class LoadIdsStepTest { } @Nonnull - private IResourcePidList createIdChunk(long idLow, long idHigh, Date lastDate) { + private IResourcePidStream createIdChunk(long idLow, long idHigh, Date lastDate) { List ids = new ArrayList<>(); List resourceTypes = new ArrayList<>(); for (long i = idLow; i < idHigh; i++) { ids.add(JpaPid.fromId(i)); } IResourcePidList chunk = new HomogeneousResourcePidList("Patient", ids, lastDate, null); - return chunk; + return new ListWrappingPidStream(chunk); } } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java index 3670bfa2bc5..1806b5428ea 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java @@ -7,7 +7,8 @@ import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.chunk.ResourceIdListWorkChunkJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; -import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -20,7 +21,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; -import java.util.Date; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -58,15 +58,13 @@ class ResourceIdListStepTest { @ParameterizedTest @ValueSource(ints = {0, 1, 100, 500, 501, 2345, 10500}) void testResourceIdListBatchSizeLimit(int theListSize) { - List idList = generateIdList(theListSize); + List idList = generateIdList(theListSize); when(myStepExecutionDetails.getData()).thenReturn(myData); - when(myParameters.getBatchSize()).thenReturn(theListSize); + when(myParameters.getBatchSize()).thenReturn(500); when(myStepExecutionDetails.getParameters()).thenReturn(myParameters); - HomogeneousResourcePidList homogeneousResourcePidList = mock(HomogeneousResourcePidList.class); + IResourcePidStream mockStream = new ListWrappingPidStream( + new HomogeneousResourcePidList("Patient", idList, null, null)); if (theListSize > 0) { - when(homogeneousResourcePidList.getTypedResourcePids()).thenReturn(idList); - when(homogeneousResourcePidList.getLastDate()).thenReturn(new Date()); - when(homogeneousResourcePidList.isEmpty()).thenReturn(false); // Ensure none of the work chunks exceed MAX_BATCH_OF_IDS in size: doAnswer(i -> { ResourceIdListWorkChunkJson list = i.getArgument(0); @@ -74,12 +72,9 @@ class ResourceIdListStepTest { "Id batch size should never exceed " + ResourceIdListStep.MAX_BATCH_OF_IDS); return null; }).when(myDataSink).accept(any(ResourceIdListWorkChunkJson.class)); - } else { - when(homogeneousResourcePidList.isEmpty()).thenReturn(true); } - when(myIdChunkProducer.fetchResourceIdsPage(any(), any(), any(), any(), any())) - .thenReturn(homogeneousResourcePidList); - + when(myIdChunkProducer.fetchResourceIdStream(any(), any(), any(), any())) + .thenReturn(mockStream); final RunOutcome run = myResourceIdListStep.run(myStepExecutionDetails, myDataSink); assertNotEquals(null, run); @@ -103,13 +98,12 @@ class ResourceIdListStepTest { } } - private List generateIdList(int theListSize) { - List idList = new ArrayList<>(); + private List generateIdList(int theListSize) { + List idList = new ArrayList<>(); for (int id = 0; id < theListSize; id++) { - IResourcePersistentId theId = mock(IResourcePersistentId.class); + IResourcePersistentId theId = mock(IResourcePersistentId.class); when(theId.toString()).thenReturn(Integer.toString(id + 1)); - TypedResourcePid typedId = new TypedResourcePid("Patient", theId); - idList.add(typedId); + idList.add(theId); } return idList; } diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java index 8411e7177a8..fcc4967c07d 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.mdm.batch2; import ca.uhn.fhir.batch2.jobs.step.IIdChunkProducer; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class MdmIdChunkProducer implements IIdChunkProducer { private static final Logger ourLog = LoggerFactory.getLogger(MdmIdChunkProducer.class); @@ -38,21 +38,17 @@ public class MdmIdChunkProducer implements IIdChunkProducer { } @Override - public IResourcePidList fetchResourceIdsPage( - Date theNextStart, - Date theEnd, - @Nonnull Integer thePageSize, - RequestPartitionId theRequestPartitionId, - MdmChunkRangeJson theData) { + public IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, MdmChunkRangeJson theData) { String resourceType = theData.getResourceType(); ourLog.info( "Fetching golden resource ID chunk for resource type {} - Range {} - {}", resourceType, - theNextStart, + theStart, theEnd); - return myGoldenResourceSearchSvc.fetchGoldenResourceIdsPage( - theNextStart, theEnd, thePageSize, theRequestPartitionId, resourceType); + return myGoldenResourceSearchSvc.fetchGoldenResourceIdStream( + theStart, theEnd, theRequestPartitionId, resourceType); } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java index 6f08db9be85..171e5cfeb8e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java @@ -51,6 +51,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.servlet.http.HttpServletResponse; @@ -356,6 +357,22 @@ public interface IFhirResourceDao extends IDao { return searchForIds(theParams, theRequest); } + /** + * Search results matching theParams. + * The Stream MUST be called within a transaction because the stream wraps an open query ResultSet. + * The Stream MUST be closed to avoid leaking resources. + * @param theParams the search + * @param theRequest for partition target info + * @return a Stream than MUST only be used within the calling transaction. + */ + default > Stream searchForIdStream( + SearchParameterMap theParams, + RequestDetails theRequest, + @Nullable IBaseResource theConditionalOperationTargetOrNull) { + List iResourcePersistentIds = searchForIds(theParams, theRequest); + return iResourcePersistentIds.stream(); + } + /** * Takes a map of incoming raw search parameters and translates/parses them into * appropriate {@link IQueryParameterType} instances of the appropriate type diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java new file mode 100644 index 00000000000..46064abdf2b --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Template for wrapping access to stream supplier in a try-with-resources block. + */ +class AutoClosingStreamTemplate implements StreamTemplate { + private final Supplier> myStreamQuery; + + AutoClosingStreamTemplate(Supplier> theStreamQuery) { + myStreamQuery = theStreamQuery; + } + + @Nullable + @Override + public R call(@Nonnull Function, R> theCallback) { + try (Stream stream = myStreamQuery.get()) { + return theCallback.apply(stream); + } + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java index 79489a0a295..00a792fcda4 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java @@ -22,7 +22,11 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -36,7 +40,9 @@ public abstract class BaseResourcePidList implements IResourcePidList { private final RequestPartitionId myRequestPartitionId; BaseResourcePidList( - Collection theIds, Date theLastDate, RequestPartitionId theRequestPartitionId) { + Collection theIds, + Date theLastDate, + RequestPartitionId theRequestPartitionId) { myIds.addAll(theIds); myLastDate = theLastDate; myRequestPartitionId = theRequestPartitionId; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java new file mode 100644 index 00000000000..9cc2926c308 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Wrapper for a query result stream. + */ +public interface IResourcePidStream { + T visitStream(Function, T> theCallback); + + default void visitStreamNoResult(Consumer> theCallback) { + visitStream(theStream -> { + theCallback.accept(theStream); + return null; + }); + } + + /** + * The partition info for the query. + */ + RequestPartitionId getRequestPartitionId(); +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java new file mode 100644 index 00000000000..6bfa5bfdd51 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class ListWrappingPidStream implements IResourcePidStream { + private final IResourcePidList myList; + + public ListWrappingPidStream(IResourcePidList theList) { + myList = theList; + } + + public Stream getTypedResourcePidStream() { + return myList.getTypedResourcePids().stream(); + } + + @Override + public T visitStream(Function, T> theCallback) { + return theCallback.apply(getTypedResourcePidStream()); + } + + @Override + public RequestPartitionId getRequestPartitionId() { + return myList.getRequestPartitionId(); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java index f14e52cad4c..7eba87c4886 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java @@ -36,7 +36,7 @@ public class MixedResourcePidList extends BaseResourcePidList { public MixedResourcePidList( List theResourceTypes, - Collection theIds, + Collection theIds, Date theLastDate, RequestPartitionId theRequestPartitionId) { super(theIds, theLastDate, theRequestPartitionId); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java new file mode 100644 index 00000000000..2559597fba6 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java @@ -0,0 +1,60 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import org.springframework.transaction.support.TransactionOperations; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * A template for stream queries, like JDBCTemplate and friends. + * + * We need to wrap access to the stream with a tx-span, a try-with-resources block, and RequestDetails. + * @param The stream content type + */ +public interface StreamTemplate { + @Nullable + R call(@Nonnull Function, R> theCallback); + + /** + * Wrap this template with a transaction boundary. + * Our dao Stream methods require an active Hibernate session for the duration of the Stream. + * This advice uses a tx boundary to ensure that active session. + * + * @param theTxBuilder the transaction and partition settings + * @return the wrapped template + */ + default StreamTemplate withTransactionAdvice(TransactionOperations theTxBuilder) { + return new TransactionWrappingStreamTemplate<>(theTxBuilder, this); + } + + /** + * Wrap the supplied stream as a StreamTemplate in a try-with-resources block to ensure it is closed. + * @param theStreamQuery the query to run + * @return a template that will always close the Stream on exit. + */ + static StreamTemplate fromSupplier(Supplier> theStreamQuery) { + return new AutoClosingStreamTemplate<>(theStreamQuery); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java new file mode 100644 index 00000000000..359b34d72bd --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java @@ -0,0 +1,52 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import org.springframework.transaction.support.TransactionOperations; + +import java.util.function.Function; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Wrap a StreamTemplate with transaction advice. + * We can't cary open ResultSets past a transaction boundary. + * This wraps a Stream producer with tx advice so the connection is still open. + */ +class TransactionWrappingStreamTemplate implements StreamTemplate { + @Nonnull + final TransactionOperations myTransaction; + + @Nonnull + final StreamTemplate myWrappedStreamTemplate; + + TransactionWrappingStreamTemplate( + @Nonnull TransactionOperations theTransaction, @Nonnull StreamTemplate theWrappedStreamTemplate) { + myTransaction = theTransaction; + this.myWrappedStreamTemplate = theWrappedStreamTemplate; + } + + @Nullable + @Override + public R call(@Nonnull Function, R> theCallback) { + return myTransaction.execute(unusedTxStatus -> myWrappedStreamTemplate.call(theCallback)); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java new file mode 100644 index 00000000000..c0ad8f92912 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 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.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class TypedResourceStream implements IResourcePidStream { + + private final RequestPartitionId myRequestPartitionId; + private final StreamTemplate myStreamSupplier; + + public TypedResourceStream( + RequestPartitionId theRequestPartitionId, StreamTemplate theStreamSupplier) { + myRequestPartitionId = theRequestPartitionId; + myStreamSupplier = theStreamSupplier; + } + + @Override + public T visitStream(Function, T> theCallback) { + return myStreamSupplier.call(theCallback); + } + + @Override + public RequestPartitionId getRequestPartitionId() { + return myRequestPartitionId; + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java index d6759d53bde..fd400883042 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.jpa.api.svc; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; import java.util.Date; import javax.annotation.Nonnull; @@ -68,4 +70,10 @@ public interface IBatch2DaoSvc { @Nullable String theUrl) { return fetchResourceIdsPage(theStart, theEnd, theRequestPartitionId, theUrl); } + + default IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, RequestPartitionId theTargetPartitionId, String theUrl) { + return new ListWrappingPidStream(fetchResourceIdsPage( + theStart, theEnd, 20000 /* ResourceIdListStep.DEFAULT_PAGE_SIZE */, theTargetPartitionId, theUrl)); + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java index 94548ae03d8..200c1e220c4 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.api.svc; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import java.util.Date; import javax.annotation.Nonnull; @@ -29,18 +29,16 @@ import javax.annotation.Nullable; public interface IGoldenResourceSearchSvc { /** - * Fetches a page of resource IDs for golden resources of the given type. The page size is up to the discretion of the implementation. + * Fetches a cursor of resource IDs for golden resources of the given type. * * @param theStart The start of the date range, must be inclusive. * @param theEnd The end of the date range, should be exclusive. - * @param thePageSize The number of golden resources to request at a time. * @param theRequestPartitionId The request partition ID (may be null on nonpartitioned systems) * @param theResourceType the type of resource. */ - IResourcePidList fetchGoldenResourceIdsPage( + IResourcePidStream fetchGoldenResourceIdStream( Date theStart, Date theEnd, - @Nonnull Integer thePageSize, @Nullable RequestPartitionId theRequestPartitionId, - @Nullable String theResourceType); + @Nonnull String theResourceType); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java index 42b1a668c2d..a44f6cc5e19 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java @@ -36,7 +36,7 @@ import java.util.Set; import javax.annotation.Nonnull; import javax.persistence.EntityManager; -public interface ISearchBuilder { +public interface ISearchBuilder> { String SEARCH_BUILDER_BEAN_NAME = "SearchBuilder"; IResultIterator createQuery( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index 56395199c9f..58a7ee5c679 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -53,6 +53,7 @@ import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionOperations; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; @@ -402,7 +403,7 @@ public class HapiTransactionService implements IHapiTransactionService { } } - protected class ExecutionBuilder implements IExecutionBuilder { + protected class ExecutionBuilder implements IExecutionBuilder, TransactionOperations { private final RequestDetails myRequestDetails; private Isolation myIsolation; @@ -473,7 +474,7 @@ public class HapiTransactionService implements IHapiTransactionService { } @Override - public T execute(TransactionCallback callback) { + public T execute(@Nonnull TransactionCallback callback) { assert callback != null; return doExecute(this, callback); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java index 6a3ca18f3b3..2f2b01b72db 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.util.ICallable; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionOperations; import java.util.concurrent.Callable; import javax.annotation.Nonnull; @@ -80,7 +81,7 @@ public interface IHapiTransactionService { @Nonnull Isolation theIsolation, @Nonnull ICallable theCallback); - interface IExecutionBuilder { + interface IExecutionBuilder extends TransactionOperations { IExecutionBuilder withIsolation(Isolation theIsolation); @@ -98,6 +99,6 @@ public interface IHapiTransactionService { T execute(Callable theTask); - T execute(TransactionCallback callback); + T execute(@Nonnull TransactionCallback callback); } } diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java new file mode 100644 index 00000000000..3161decc75d --- /dev/null +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.jpa.api.pid; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class AutoClosingStreamTemplateTest { + + @Test + void templatePassesStreamToCallback() { + // given + Stream concreteStream = Stream.of("one", "two"); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + streamTemplate.call(s -> { + assertSame(concreteStream, s); + return 0; + }); + } + + @Test + void templateClosesStreamOnExit() { + // given + AtomicBoolean wasClosed = new AtomicBoolean(false); + Stream concreteStream = Stream.of("one", "two") + .onClose(()->wasClosed.set(true)); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + streamTemplate.call(s -> { + // don't touch the stream; + return 0; + }); + + assertTrue(wasClosed.get(), "stream was closed"); + + } + + + @Test + void templateClosesStreamOnException() { + // given + AtomicBoolean wasClosed = new AtomicBoolean(false); + Stream concreteStream = Stream.of("one", "two") + .onClose(()->wasClosed.set(true)); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + try { + streamTemplate.call(s -> { + throw new RuntimeException("something failed"); + }); + } catch (RuntimeException e) { + // expected; + } + + assertTrue(wasClosed.get(), "stream was closed"); + + } + +} From d5a1b0cdf3de6f449132c5e54ddecfdfcfc11106 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 21 Nov 2023 11:39:18 -0500 Subject: [PATCH 35/86] fix terserutil clear (#5473) * fix test * review --- .../java/ca/uhn/fhir/util/TerserUtil.java | 2 +- .../java/ca/uhn/fhir/util/TerserUtilTest.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java index d20fe991c16..434e085fb95 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java @@ -348,7 +348,7 @@ public final class TerserUtil { public static void clearField(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); - clear(childDefinition.getAccessor().getValues(theResource)); + childDefinition.getMutator().setValue(theResource, null); } /** diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java index ddf94c26105..b387b11b5a0 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TerserUtilTest.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.r4.model.Address; +import org.hl7.fhir.r4.model.Claim; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.Enumerations; @@ -13,6 +14,7 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.PrimitiveType; +import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; @@ -570,6 +572,25 @@ class TerserUtilTest { assertEquals("Test", p1.getAddress().get(1).getCity()); } + @Test + void testRemoveByFhirPath() { + // arrange + Claim claimWithReferences = createClaim(); + claimWithReferences.setPatient(new Reference("Patient/123")); + String fhirPath = "patient"; + assertTrue(claimWithReferences.hasPatient()); + //act + TerserUtil.clearFieldByFhirPath(ourFhirContext, claimWithReferences, fhirPath); + //assert + assertFalse(claimWithReferences.hasPatient()); + } + + static Claim createClaim() { + Claim claim = new Claim(); + claim.setStatus(Claim.ClaimStatus.ACTIVE); + return claim; + } + @Test public void testSetField() { Patient p1 = new Patient(); From f519f17fc568c2ce780718ffe15752670a2963c3 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 21 Nov 2023 12:36:06 -0500 Subject: [PATCH 36/86] Remove mandatory caffeine dependency (#5472) * Remove mandatory caffeine dependency * Add changelog * Test fix --- ...ffeine-dependency-in-validator-module.yaml | 5 +++++ hapi-fhir-storage/pom.xml | 6 +++++ hapi-fhir-validation/pom.xml | 22 ++++++------------- 3 files changed, 18 insertions(+), 15 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml new file mode 100644 index 00000000000..2680269879b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml @@ -0,0 +1,5 @@ +--- +type: fix +title: "The hapi-fhir-validation module inadvertently included a mandatory dependency on the Caffeine + caching library instead of leaving it to implementors to choose which module to pick. This has been + corrected." diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index d404c922d58..2aa2bc719cf 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -160,6 +160,12 @@ org.springframework.data spring-data-commons
    + + ca.uhn.hapi.fhir + hapi-fhir-caching-caffeine + ${project.version} + test + diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 2b22ccaa0e5..e4ec72d25c0 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -26,17 +26,11 @@ hapi-fhir-converter ${project.version}
    - ca.uhn.hapi.fhir hapi-fhir-caching-api ${project.version} - - ca.uhn.hapi.fhir - hapi-fhir-caching-caffeine - ${project.version} - ca.uhn.hapi.fhir @@ -82,16 +76,13 @@ - - - ca.uhn.hapi.fhir org.hl7.fhir.utilities ${fhir_core_version} - + org.fhir ucum @@ -102,11 +93,6 @@ - - com.github.ben-manes.caffeine - caffeine - - com.google.code.findbugs jsr305 @@ -351,6 +337,12 @@ ${project.version} test + + ca.uhn.hapi.fhir + hapi-fhir-caching-caffeine + ${project.version} + test + From 9aa173a981c46bed3e141e1862ce9f5708e6d5da Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Tue, 21 Nov 2023 19:12:38 -0500 Subject: [PATCH 37/86] Target the default partition for batch2 storage. (#5475) --- .../7_0_0/5475-batch-default-partition.yaml | 4 + .../jpa/batch2/JpaJobPersistenceImpl.java | 81 ++++++++++--------- .../jpa/test/PatientReindexTestHelper.java | 15 ++-- .../batch2/api/IWorkChunkPersistence.java | 3 +- .../coordinator/JobCoordinatorImpl.java | 6 +- .../WorkChannelMessageHandler.java | 68 ++++++++-------- .../jpa/dao/tx/IHapiTransactionService.java | 9 ++- 7 files changed, 105 insertions(+), 81 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml new file mode 100644 index 00000000000..4cb01887bb8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5475 +title: "Ensure batch2 jpa persistence always targets the default partition." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java index 01fe091dbbe..a90a88cb4f9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java @@ -115,7 +115,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { ourLog.debug("Create work chunk {}/{}/{}", entity.getInstanceId(), entity.getId(), entity.getTargetStepId()); ourLog.trace( "Create work chunk data {}/{}: {}", entity.getInstanceId(), entity.getId(), entity.getSerializedData()); - myWorkChunkRepository.save(entity); + myTransactionService.withSystemRequestOnDefaultPartition().execute(() -> myWorkChunkRepository.save(entity)); return entity.getId(); } @@ -209,7 +209,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { @Nonnull public Optional fetchInstance(String theInstanceId) { return myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() .execute(() -> myJobInstanceRepository.findById(theInstanceId).map(this::toInstance)); } @@ -225,7 +225,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { List instanceEntities; if (statuses != null && !statuses.isEmpty()) { - if (definitionId.equals(Batch2JobDefinitionConstants.BULK_EXPORT)) { + if (Batch2JobDefinitionConstants.BULK_EXPORT.equals(definitionId)) { if (originalRequestUrlTruncation(params) != null) { params = originalRequestUrlTruncation(params); } @@ -268,18 +268,22 @@ public class JpaJobPersistenceImpl implements IJobPersistence { public List fetchInstances(int thePageSize, int thePageIndex) { // default sort is myCreateTime Asc PageRequest pageRequest = PageRequest.of(thePageIndex, thePageSize, Sort.Direction.ASC, CREATE_TIME); - return myJobInstanceRepository.findAll(pageRequest).stream() - .map(this::toInstance) - .collect(Collectors.toList()); + return myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myJobInstanceRepository.findAll(pageRequest).stream() + .map(this::toInstance) + .collect(Collectors.toList())); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public List fetchRecentInstances(int thePageSize, int thePageIndex) { PageRequest pageRequest = PageRequest.of(thePageIndex, thePageSize, Sort.Direction.DESC, CREATE_TIME); - return myJobInstanceRepository.findAll(pageRequest).stream() - .map(this::toInstance) - .collect(Collectors.toList()); + return myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myJobInstanceRepository.findAll(pageRequest).stream() + .map(this::toInstance) + .collect(Collectors.toList())); } private WorkChunk toChunk(Batch2WorkChunkEntity theEntity) { @@ -295,44 +299,49 @@ public class JpaJobPersistenceImpl implements IJobPersistence { public WorkChunkStatusEnum onWorkChunkError(WorkChunkErrorEvent theParameters) { String chunkId = theParameters.getChunkId(); String errorMessage = truncateErrorMessage(theParameters.getErrorMsg()); - int changeCount = myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( - chunkId, new Date(), errorMessage, WorkChunkStatusEnum.ERRORED); - Validate.isTrue(changeCount > 0, "changed chunk matching %s", chunkId); - Query query = myEntityManager.createQuery("update Batch2WorkChunkEntity " + "set myStatus = :failed " - + ",myErrorMessage = CONCAT('Too many errors: ', CAST(myErrorCount as string), '. Last error msg was ', myErrorMessage) " - + "where myId = :chunkId and myErrorCount > :maxCount"); - query.setParameter("chunkId", chunkId); - query.setParameter("failed", WorkChunkStatusEnum.FAILED); - query.setParameter("maxCount", MAX_CHUNK_ERROR_COUNT); - int failChangeCount = query.executeUpdate(); + return myTransactionService.withSystemRequestOnDefaultPartition().execute(() -> { + int changeCount = myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( + chunkId, new Date(), errorMessage, WorkChunkStatusEnum.ERRORED); + Validate.isTrue(changeCount > 0, "changed chunk matching %s", chunkId); - if (failChangeCount > 0) { - return WorkChunkStatusEnum.FAILED; - } else { - return WorkChunkStatusEnum.ERRORED; - } + Query query = myEntityManager.createQuery("update Batch2WorkChunkEntity " + "set myStatus = :failed " + + ",myErrorMessage = CONCAT('Too many errors: ', CAST(myErrorCount as string), '. Last error msg was ', myErrorMessage) " + + "where myId = :chunkId and myErrorCount > :maxCount"); + query.setParameter("chunkId", chunkId); + query.setParameter("failed", WorkChunkStatusEnum.FAILED); + query.setParameter("maxCount", MAX_CHUNK_ERROR_COUNT); + int failChangeCount = query.executeUpdate(); + + if (failChangeCount > 0) { + return WorkChunkStatusEnum.FAILED; + } else { + return WorkChunkStatusEnum.ERRORED; + } + }); } @Override - @Transactional public void onWorkChunkFailed(String theChunkId, String theErrorMessage) { ourLog.info("Marking chunk {} as failed with message: {}", theChunkId, theErrorMessage); String errorMessage = truncateErrorMessage(theErrorMessage); - myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( - theChunkId, new Date(), errorMessage, WorkChunkStatusEnum.FAILED); + myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( + theChunkId, new Date(), errorMessage, WorkChunkStatusEnum.FAILED)); } @Override - @Transactional public void onWorkChunkCompletion(WorkChunkCompletionEvent theEvent) { - myWorkChunkRepository.updateChunkStatusAndClearDataForEndSuccess( - theEvent.getChunkId(), - new Date(), - theEvent.getRecordsProcessed(), - theEvent.getRecoveredErrorCount(), - WorkChunkStatusEnum.COMPLETED, - theEvent.getRecoveredWarningMessage()); + myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myWorkChunkRepository.updateChunkStatusAndClearDataForEndSuccess( + theEvent.getChunkId(), + new Date(), + theEvent.getRecordsProcessed(), + theEvent.getRecoveredErrorCount(), + WorkChunkStatusEnum.COMPLETED, + theEvent.getRecoveredWarningMessage())); } @Nullable @@ -389,7 +398,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { int thePageIndex, Consumer theConsumer) { myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() .withPropagation(Propagation.REQUIRES_NEW) .execute(() -> { List chunks; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java index 684b428a784..5f877ac815e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java @@ -27,10 +27,10 @@ import ca.uhn.fhir.batch2.model.JobInstanceStartRequest; import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.params.provider.Arguments; @@ -52,6 +52,7 @@ public class PatientReindexTestHelper { private final Batch2JobHelper myBatch2JobHelper; private final IFhirResourceDao myPatientDao; private final boolean myIncrementVersionOnReindex; + private final RequestDetails myRequestDetails = new SystemRequestDetails(); public static Stream numResourcesParams(){ return Stream.of( @@ -79,7 +80,7 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance1, theNumResources); @@ -95,7 +96,7 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance1, theNumResources); @@ -104,7 +105,7 @@ public class PatientReindexTestHelper { // Reindex 2 JobInstanceStartRequest reindexRequest2 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(reindexRequest2); + Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest2); JobInstance instance2 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse2.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance2, theNumResources); @@ -119,11 +120,11 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); // Reindex 2 JobInstanceStartRequest reindexRequest2 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(reindexRequest2); + Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest2); // Wait for jobs to finish JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); @@ -170,7 +171,7 @@ public class PatientReindexTestHelper { startRequest.setJobDefinitionId(ReindexAppCtx.JOB_REINDEX); ReindexJobParameters reindexJobParameters = new ReindexJobParameters(); - reindexJobParameters.setBatchSize(theBatchSize); + reindexJobParameters.setBatchSize(Math.max(theBatchSize,1)); reindexJobParameters.addUrl("Patient?"); startRequest.setParameters(reindexJobParameters); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java index d29a476c214..1850a4bfb4c 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java @@ -60,7 +60,6 @@ public interface IWorkChunkPersistence { * @param theBatchWorkChunk the batch work chunk to be stored * @return a globally unique identifier for this chunk. */ - @Transactional(propagation = Propagation.REQUIRED) String onWorkChunkCreate(WorkChunkCreateEvent theBatchWorkChunk); /** @@ -71,7 +70,7 @@ public interface IWorkChunkPersistence { * @param theChunkId The ID from {@link #onWorkChunkCreate} * @return The WorkChunk or empty if no chunk exists, or not in a runnable state (QUEUED or ERRORRED) */ - @Transactional(propagation = Propagation.REQUIRED) + @Transactional(propagation = Propagation.MANDATORY) Optional onWorkChunkDequeue(String theChunkId); /** diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java index 23cf995fa6d..b51732df3be 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java @@ -43,7 +43,7 @@ import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.springframework.data.domain.Page; import org.springframework.messaging.MessageHandler; -import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Arrays; @@ -143,7 +143,7 @@ public class JobCoordinatorImpl implements IJobCoordinator { myJobParameterJsonValidator.validateJobParameters(theRequestDetails, theStartRequest, jobDefinition); IJobPersistence.CreateResult instanceAndFirstChunk = myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() .execute(() -> myJobPersistence.onCreateWithFirstChunk(jobDefinition, theStartRequest.getParameters())); JobWorkNotification workNotification = JobWorkNotification.firstStepNotification( @@ -163,7 +163,7 @@ public class JobCoordinatorImpl implements IJobCoordinator { */ private void sendBatchJobWorkNotificationAfterCommit(final JobWorkNotification theJobWorkNotification) { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public int getOrder() { return 0; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java index 21d5f2cba87..7f9ccd6ac3a 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java @@ -231,46 +231,50 @@ class WorkChannelMessageHandler implements MessageHandler { // We use Optional chaining here to simplify all the cases where we short-circuit exit. // A step that returns an empty Optional means discard the chunk. // - executeInTxRollbackWhenEmpty(() -> - ( - // Use a chain of Optional flatMap to handle all the setup short-circuit exits cleanly. - Optional.of(new MessageProcess(workNotification)) - // validate and load info - .flatMap(MessageProcess::validateChunkId) - // no job definition should be retried - we must be a stale process encountering a new - // job definition. - .flatMap(MessageProcess::loadJobDefinitionOrThrow) - .flatMap(MessageProcess::loadJobInstance) - // update statuses now in the db: QUEUED->IN_PROGRESS - .flatMap(MessageProcess::updateChunkStatusAndValidate) - .flatMap(MessageProcess::updateAndValidateJobStatus) - // ready to execute - .flatMap(MessageProcess::buildCursor) - .flatMap(MessageProcess::buildStepExecutor))) - .ifPresentOrElse( - // all the setup is happy and committed. Do the work. - process -> process.myStepExector.executeStep(), - // discard the chunk - () -> ourLog.debug("Discarding chunk notification {}", workNotification)); + Optional processingPreparation = executeInTxRollbackWhenEmpty(() -> + + // Use a chain of Optional flatMap to handle all the setup short-circuit exits cleanly. + Optional.of(new MessageProcess(workNotification)) + // validate and load info + .flatMap(MessageProcess::validateChunkId) + // no job definition should be retried - we must be a stale process encountering a new + // job definition. + .flatMap(MessageProcess::loadJobDefinitionOrThrow) + .flatMap(MessageProcess::loadJobInstance) + // update statuses now in the db: QUEUED->IN_PROGRESS + .flatMap(MessageProcess::updateChunkStatusAndValidate) + .flatMap(MessageProcess::updateAndValidateJobStatus) + // ready to execute + .flatMap(MessageProcess::buildCursor) + .flatMap(MessageProcess::buildStepExecutor)); + + processingPreparation.ifPresentOrElse( + // all the setup is happy and committed. Do the work. + process -> process.myStepExector.executeStep(), + // discard the chunk + () -> ourLog.debug("Discarding chunk notification {}", workNotification)); } /** * Run theCallback in TX, rolling back if the supplied Optional is empty. */ Optional executeInTxRollbackWhenEmpty(Supplier> theCallback) { - return myHapiTransactionService.withSystemRequest().execute(theTransactionStatus -> { + return myHapiTransactionService + // batch storage is not partitioned. + .withSystemRequestOnDefaultPartition() + .execute(theTransactionStatus -> { - // run the processing - Optional setupProcessing = theCallback.get(); + // run the processing + Optional setupProcessing = theCallback.get(); - if (setupProcessing.isEmpty()) { - // If any setup failed, roll back the chunk and instance status changes. - ourLog.debug("WorkChunk setup failed - rollback tx"); - theTransactionStatus.setRollbackOnly(); - } - // else COMMIT the work. + if (setupProcessing.isEmpty()) { + // If any setup failed, roll back the chunk and instance status changes. + ourLog.debug("WorkChunk setup failed - rollback tx"); + theTransactionStatus.setRollbackOnly(); + } + // else COMMIT the work. - return setupProcessing; - }); + return setupProcessing; + }); } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java index 2f2b01b72db..5ac40e274b9 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java @@ -70,10 +70,17 @@ public interface IHapiTransactionService { return withSystemRequest().withRequestPartitionId(theRequestPartitionId); } + /** + * Convenience for TX working with non-partitioned entities. + */ + default IExecutionBuilder withSystemRequestOnDefaultPartition() { + return withSystemRequestOnPartition(RequestPartitionId.defaultPartition()); + } + /** * @deprecated It is highly recommended to use {@link #withRequest(RequestDetails)} instead of this method, for increased visibility. */ - @Deprecated + @Deprecated(since = "6.10") T withRequest( @Nullable RequestDetails theRequestDetails, @Nullable TransactionDetails theTransactionDetails, From 86ea0694376c7a8e1a7f9ef0e2b0a0765819fdc0 Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Wed, 22 Nov 2023 08:30:51 -0700 Subject: [PATCH 38/86] feature swap to non javax clinical reasoning (#5463) * Update to a version of Clinical Reasoning/CQL that no longer includes javax dependencies * Rev to CR 3.0.0-PRE10 * version bump, pom changes for jakarta conflicts * undo exclusions from other modules * Rework exclusions * Fix type in dependency * new cr version, exclude conflicts in cr and cds-hooks * remove exclusions * pom update * Redo the exclusions * add retrieveSettings config * new CR retrieve settings and config * update config to new enums and parameters * update unit tests and config for new cql and terminology properties * bump CR to 3.0.0-PRE12 * spotless edits, update class path for CR config * missing dependencies * swap for jakarta * moving conflicting dependencies * Fix a couple code review items --------- Co-authored-by: Jonathan Percival --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 14 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- ...-swap-to-non-javax-clinical-reasoning.yaml | 4 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 3 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 26 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 97 +- .../cr/config/dstu3/CrProcessorConfig.java | 2 +- .../fhir/cr/config/r4/CrProcessorConfig.java | 2 +- .../dstu3/IQuestionnaireProcessorFactory.java | 2 +- .../measure/MeasureOperationsProvider.java | 3 + .../cr/r4/IQuestionnaireProcessorFactory.java | 2 +- .../r4/measure/MeasureOperationsProvider.java | 3 + .../java/ca/uhn/fhir/cr/TestCrConfig.java | 16 +- .../uhn/fhir/cr/dstu3/TestCrDstu3Config.java | 26 +- .../fhir/cr/r4/CrResourceListenerTests.java | 6 +- .../ca/uhn/fhir/cr/r4/TestCrR4Config.java | 34 +- .../largeValueSetMeasureTest-Bundle.json | 26528 ++++++++-------- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 27 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 89 files changed, 13508 insertions(+), 13435 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 34d7898f84c..6f8821871d0 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 3415ba0b4fd..a709106324f 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 6411069d373..49356c507ab 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index c3f812a01bd..81e7e9aec1f 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 37a6009c2b0..b9317cac55f 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-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 92fa2dc2346..3cdf6295cf1 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../../hapi-deployable-pom/pom.xml @@ -257,7 +257,17 @@ spring-test test - + + org.simplejavamail + simple-java-mail + + + + com.sun.activation + jakarta.activation + + + diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index d9b77e0fae5..a1446d3b7e9 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 386cda0f5f4..a7602e782d2 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index a632a1bed98..08cf10fe4e4 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 21ccd151f43..f3841700d54 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 2e46ecadfba..62bfffc3449 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 04ecb9a4bf6..9f5e2d97868 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 16d49e4858d..04a44e817a0 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml new file mode 100644 index 00000000000..e479130e80a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5436 +title: "Moving clinical reasoning to non-javax dependency for alignment with incoming hapi-fhir changes, bumping CR version and updating impacted classes and services" diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index fe201071976..8b14ff12758 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 168fad35745..ac6893ad3c7 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 0a404648b65..a525ae31628 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 56693416e0a..f9c3baca44f 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index c336374b3eb..3d76846bda2 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 8104243a146..43c285f9e4e 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 84849832b0f..97ec14f2cc6 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 74119cc15e0..dd626c0728c 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 1e3cd6500a1..ee576bed519 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 985f107995b..fe4185c5e32 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 3c7c5a5401b..ee8d7e75b73 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 - 6.11.0-SNAPSHOT + 6.11.1-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 8fc1cc0cb54..1b49c97a911 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index ba294c354e2..cc466d934e8 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 141e3c3ec76..163ec756332 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index b0e6eb43e37..dda2f61c2fa 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 940538b889d..b5c3dbed217 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 2a647677748..336c98f999e 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -259,7 +259,6 @@
    - diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 04854c03ab5..af8ccff2927 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 61f9a18c007..171e049604d 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -38,7 +38,6 @@ hapi-fhir-storage-cr ${project.version} - org.springframework spring-beans @@ -77,6 +76,29 @@ ${reflections_version} test + + org.simplejavamail + simple-java-mail + + + + com.sun.activation + jakarta.activation + + + + + com.icegreen + greenmail + compile + + + + com.sun.activation + jakarta.activation + + + diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 843717757f7..af536a1acbb 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index c50340dc1a6..286e6387ab0 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 1f4e3686315..e13e1a075df 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 5971966a2ef..df6b241f45f 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 - 6.11.0-SNAPSHOT + 6.11.1-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 63db8746c57..6414256d3aa 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 02aa7a66f9b..085610ef2be 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 - 6.11.0-SNAPSHOT + 6.11.1-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 3755df79214..39e70955007 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index bba7b7c6c9c..a2cf0594f98 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.11.0-SNAPSHOT + 6.11.1-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 4e50b66568c..616ab7f9836 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 - 6.11.0-SNAPSHOT + 6.11.1-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 a05240a022f..aa4731815a4 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 - 6.11.0-SNAPSHOT + 6.11.1-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 94495f02513..6ee088ed768 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 - 6.11.0-SNAPSHOT + 6.11.1-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 f69a1d9c9a7..280627421c8 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 - 6.11.0-SNAPSHOT + 6.11.1-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 24b0b2136c1..f8d1e7f6796 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 - 6.11.0-SNAPSHOT + 6.11.1-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 332a8c31eae..59dabbf94fd 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index c5dc0e3d5d3..87b749a9885 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index d76ab3688d6..efb9e42a2f2 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 21c0d51fce4..a0ddd051b40 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index fa949045515..23bc7cff5a9 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 77273b31422..de8dcda41e1 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index afec2b537cb..b5f9ecce3c5 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -19,7 +19,6 @@ 4.10.1 - 1.1.6 5.7.8 @@ -66,6 +65,31 @@ hapi-fhir-base ${project.version} + + com.icegreen + greenmail + 1.6.4 + compile + + + + com.sun.activation + jakarta.activation + + + + + org.simplejavamail + simple-java-mail + 6.6.1 + + + + com.sun.activation + jakarta.activation + + + @@ -84,52 +108,23 @@ org.opencds.cqf.fhir cqf-fhir-jackson ${clinical-reasoning.version} - - - xpp3 - xpp3_min - - - xmlpull - xmlpull - - - xmlpull - xmlpull - - pom - - org.opencds.cqf.fhir - cqf-fhir-cql - ${clinical-reasoning.version} - - - xpp3 - xpp3_min - - - xmlpull - xmlpull - - - xmlpull - xmlpull - - - pom - - - org.ogce - xpp3 - ${xpp3-version} - - ca.uhn.hapi.fhir hapi-fhir-storage ${project.version} + + + com.sun.activation + jakarta.activation + + + + + + org.apache.poi + poi @@ -139,11 +134,23 @@ ${spring-security-core.version} - + javax.servlet javax.servlet-api + + javax.xml.bind + jaxb-api + 2.3.1 + + @@ -185,10 +192,6 @@ ${project.version} test - - org.apache.poi - poi - diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java index b039eb9514e..3d87c18b698 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java @@ -43,7 +43,7 @@ public class CrProcessorConfig { @Bean ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.processor.QuestionnaireProcessor( theRepositoryFactory.create(rd), theEvaluationSettings); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java index 6632a244c39..dffd4b2f68b 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java @@ -43,7 +43,7 @@ public class CrProcessorConfig { @Bean ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.processor.QuestionnaireProcessor( theRepositoryFactory.create(rd), theEvaluationSettings); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java index 3cc35c83356..3ee321958ca 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.cr.dstu3; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor; +import org.opencds.cqf.fhir.cr.questionnaire.dstu3.processor.QuestionnaireProcessor; @FunctionalInterface public interface IQuestionnaireProcessorFactory { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java index e6ca48d241a..5130d6030ea 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java @@ -31,6 +31,7 @@ import org.hl7.fhir.dstu3.model.Endpoint; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Measure; import org.hl7.fhir.dstu3.model.MeasureReport; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.exceptions.FHIRException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -76,6 +77,7 @@ public class MeasureOperationsProvider { @OperationParam(name = "productLine") String theProductLine, @OperationParam(name = "additionalData") Bundle theAdditionalData, @OperationParam(name = "terminologyEndpoint") Endpoint theTerminologyEndpoint, + @OperationParam(name = "parameters") Parameters theParameters, RequestDetails theRequestDetails) throws InternalErrorException, FHIRException { return myDstu3MeasureProcessorFactory @@ -90,6 +92,7 @@ public class MeasureOperationsProvider { theLastReceivedOn, theProductLine, theAdditionalData, + theParameters, theTerminologyEndpoint); } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java index 6105ea61517..82d77f2b956 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor; +import org.opencds.cqf.fhir.cr.questionnaire.r4.processor.QuestionnaireProcessor; @FunctionalInterface public interface IQuestionnaireProcessorFactory { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java index 5526dc8f1dd..0a533224e3e 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java @@ -32,6 +32,7 @@ import org.hl7.fhir.r4.model.Endpoint; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.MeasureReport; +import org.hl7.fhir.r4.model.Parameters; import org.opencds.cqf.fhir.utility.monad.Eithers; import org.springframework.beans.factory.annotation.Autowired; @@ -74,6 +75,7 @@ public class MeasureOperationsProvider { @OperationParam(name = "productLine") String theProductLine, @OperationParam(name = "additionalData") Bundle theAdditionalData, @OperationParam(name = "terminologyEndpoint") Endpoint theTerminologyEndpoint, + @OperationParam(name = "parameters") Parameters theParameters, RequestDetails theRequestDetails) throws InternalErrorException, FHIRException { return myR4MeasureServiceFactory @@ -89,6 +91,7 @@ public class MeasureOperationsProvider { theTerminologyEndpoint, null, theAdditionalData, + theParameters, theProductLine, thePractitioner); } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java index 0d801b46332..4f42ae49d52 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java @@ -3,21 +3,17 @@ package ca.uhn.fhir.cr; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; - import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener; import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.cache.IResourceChangeListener; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCacheRefresher; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; -import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCache; import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory; import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor; -import ca.uhn.fhir.jpa.cache.ResourceVersionMap; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; @@ -34,32 +30,22 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.base.Strings; -import org.cqframework.cql.cql2elm.LibraryManager; import org.cqframework.cql.cql2elm.ModelManager; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.Model; - import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.Scope; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - @Configuration @Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class}) @@ -168,4 +154,6 @@ public class TestCrConfig { return new ResourceChangeListenerRegistryInterceptor(); } + + } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java index 0895237526d..67a8c1a6f82 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java @@ -16,6 +16,8 @@ import org.opencds.cqf.cql.engine.execution.CqlEngine; import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.utility.ValidationProfile; import org.springframework.context.annotation.Bean; @@ -43,11 +45,31 @@ public class TestCrDstu3Config { } return measureEvalOptions; } + @Bean + public TerminologySettings terminologySettings(){ + var termSettings = new TerminologySettings(); + termSettings.setCodeLookupMode(TerminologySettings.CODE_LOOKUP_MODE.USE_CODESYSTEM_URL); + termSettings.setValuesetExpansionMode(TerminologySettings.VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + termSettings.setValuesetMembershipMode(TerminologySettings.VALUESET_MEMBERSHIP_MODE.USE_EXPANSION); + termSettings.setValuesetPreExpansionMode(TerminologySettings.VALUESET_PRE_EXPANSION_MODE.USE_IF_PRESENT); + return termSettings; + } + @Bean + public RetrieveSettings retrieveSettings(){ + var retrieveSettings = new RetrieveSettings(); + retrieveSettings.setSearchParameterMode(RetrieveSettings.SEARCH_FILTER_MODE.USE_SEARCH_PARAMETERS); + retrieveSettings.setTerminologyParameterMode(RetrieveSettings.TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + retrieveSettings.setProfileMode(RetrieveSettings.PROFILE_MODE.OFF); + + return retrieveSettings; + } @Bean public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, - Map> theGlobalValueSetCache) { + Map> theGlobalValueSetCache, + RetrieveSettings theRetrieveSettings, + TerminologySettings theTerminologySettings) { var evaluationSettings = EvaluationSettings.getDefault(); var cqlOptions = evaluationSettings.getCqlOptions(); @@ -112,6 +134,8 @@ public class TestCrDstu3Config { cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); + evaluationSettings.setTerminologySettings(theTerminologySettings); + evaluationSettings.setRetrieveSettings(theRetrieveSettings); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); evaluationSettings.setValueSetCache(theGlobalValueSetCache); diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java index b10071ce244..0a637103901 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java @@ -59,7 +59,7 @@ public class CrResourceListenerTests extends BaseCrR4TestServer { myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); //cached valueSets - assertEquals(11, myEvaluationSettings.getValueSetCache().size()); + assertEquals(7, myEvaluationSettings.getValueSetCache().size()); //remove valueset from server var id = new IdType("ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001"); @@ -158,7 +158,7 @@ public class CrResourceListenerTests extends BaseCrR4TestServer { runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); //cached valueset from bundle - assertEquals(11, myEvaluationSettings.getValueSetCache().size()); + assertEquals(8, myEvaluationSettings.getValueSetCache().size()); // manually refresh cache myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); @@ -170,7 +170,7 @@ public class CrResourceListenerTests extends BaseCrR4TestServer { myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); //cache should be invalidated for valueset url and removed - assertEquals(10, myEvaluationSettings.getValueSetCache().size()); + assertEquals(7, myEvaluationSettings.getValueSetCache().size()); } } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java index d3be5bab245..c6efed8ddee 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java @@ -3,11 +3,8 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.cr.TestCqlProperties; import ca.uhn.fhir.cr.TestCrConfig; import ca.uhn.fhir.cr.common.CqlThreadFactory; -import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; -import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig; -import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; -import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.cr.config.r4.CrR4Config; +import ca.uhn.fhir.rest.api.SearchStyleEnum; import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.Model; @@ -16,6 +13,9 @@ import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.execution.CqlEngine; import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.engine.retrieve.BaseRetrieveProvider; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.utility.ValidationProfile; @@ -64,10 +64,32 @@ public class TestCrR4Config { } return measureEvalOptions; } + + @Bean + public TerminologySettings terminologySettings(){ + var termSettings = new TerminologySettings(); + termSettings.setCodeLookupMode(TerminologySettings.CODE_LOOKUP_MODE.USE_CODESYSTEM_URL); + termSettings.setValuesetExpansionMode(TerminologySettings.VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + termSettings.setValuesetMembershipMode(TerminologySettings.VALUESET_MEMBERSHIP_MODE.USE_EXPANSION); + termSettings.setValuesetPreExpansionMode(TerminologySettings.VALUESET_PRE_EXPANSION_MODE.USE_IF_PRESENT); + + return termSettings; + } + @Bean + public RetrieveSettings retrieveSettings(){ + var retrieveSettings = new RetrieveSettings(); + retrieveSettings.setSearchParameterMode(RetrieveSettings.SEARCH_FILTER_MODE.USE_SEARCH_PARAMETERS); + retrieveSettings.setTerminologyParameterMode(RetrieveSettings.TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + retrieveSettings.setProfileMode(RetrieveSettings.PROFILE_MODE.OFF); + + return retrieveSettings; + } @Bean public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, - Map> theGlobalValueSetCache) { + Map> theGlobalValueSetCache, + RetrieveSettings theRetrieveSettings, + TerminologySettings theTerminologySettings) { var evaluationSettings = EvaluationSettings.getDefault(); var cqlOptions = evaluationSettings.getCqlOptions(); @@ -132,6 +154,8 @@ public class TestCrR4Config { cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); + evaluationSettings.setTerminologySettings(theTerminologySettings); + evaluationSettings.setRetrieveSettings(theRetrieveSettings); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); evaluationSettings.setValueSetCache(theGlobalValueSetCache); diff --git a/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json b/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json index bd9ba5bb084..a4785ce2534 100644 --- a/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json +++ b/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json @@ -1,13276 +1,13256 @@ { - "resourceType": "Bundle", - "id": "CMSTest-bundle", - "type": "transaction", - "entry": [ - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.837", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.837" - } - ], - "version": "20121025", - "name": "Ethnicity", - "title": "Ethnicity", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Ethnicity.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2135-2", - "display": "Hispanic or Latino" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2186-5", - "display": "Not Hispanic or Latino" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.837" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.3591", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.3591" - } - ], - "version": "20180718", - "name": "Payer", - "title": "Payer", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Payer.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "1", - "display": "MEDICARE" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "11", - "display": "Medicare Managed Care (Includes Medicare Advantage Plans)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "111", - "display": "Medicare HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "112", - "display": "Medicare PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "113", - "display": "Medicare POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "119", - "display": "Medicare Managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "12", - "display": "Medicare (Non-managed Care)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "121", - "display": "Medicare FFS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "122", - "display": "Medicare Drug Benefit" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "123", - "display": "Medicare Medical Savings Account (MSA)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "129", - "display": "Medicare Non-managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "13", - "display": "Medicare Hospice" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "14", - "display": "Dual Eligibility Medicare/Medicaid Organization" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "19", - "display": "Medicare Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "191", - "display": "Medicare Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "2", - "display": "MEDICAID" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "21", - "display": "Medicaid (Managed Care)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "211", - "display": "Medicaid HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "212", - "display": "Medicaid PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "213", - "display": "Medicaid PCCM (Primary Care Case Management)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "219", - "display": "Medicaid Managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "22", - "display": "Medicaid (Non-managed Care Plan)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "23", - "display": "Medicaid/SCHIP" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "25", - "display": "Medicaid - Out of State" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "26", - "display": "Medicaid - Long Term Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "29", - "display": "Medicaid Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "291", - "display": "Medicaid Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "299", - "display": "Medicaid - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3", - "display": "OTHER GOVERNMENT (Federal/State/Local) (excluding Department of Corrections)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "31", - "display": "Department of Defense" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "311", - "display": "TRICARE (CHAMPUS)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3111", - "display": "TRICARE Prime--HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3112", - "display": "TRICARE Extra--PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3113", - "display": "TRICARE Standard - Fee For Service" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3114", - "display": "TRICARE For Life--Medicare Supplement" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3115", - "display": "TRICARE Reserve Select" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3116", - "display": "Uniformed Services Family Health Plan (USFHP) -- HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3119", - "display": "Department of Defense - (other)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "312", - "display": "Military Treatment Facility" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3121", - "display": "Enrolled Prime--HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3122", - "display": "Non-enrolled Space Available" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3123", - "display": "TRICARE For Life (TFL)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "313", - "display": "Dental --Stand Alone" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32", - "display": "Department of Veterans Affairs" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "321", - "display": "Veteran care-Care provided to Veterans" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3211", - "display": "Direct Care-Care provided in VA facilities" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3212", - "display": "Indirect Care-Care provided outside VA facilities" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32121", - "display": "Fee Basis" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32122", - "display": "Foreign Fee/Foreign Medical Program (FMP)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32123", - "display": "Contract Nursing Home/Community Nursing Home" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32124", - "display": "State Veterans Home" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32125", - "display": "Sharing Agreements" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32126", - "display": "Other Federal Agency" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32127", - "display": "Dental Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32128", - "display": "Vision Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "322", - "display": "Non-veteran care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3221", - "display": "Civilian Health and Medical Program for the VA (CHAMPVA)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3222", - "display": "Spina Bifida Health Care Program (SB)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3223", - "display": "Children of Women Vietnam Veterans (CWVV)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3229", - "display": "Other non-veteran care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "33", - "display": "Indian Health Service or Tribe" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "331", - "display": "Indian Health Service - Regular" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "332", - "display": "Indian Health Service - Contract" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "333", - "display": "Indian Health Service - Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "334", - "display": "Indian Tribe - Sponsored Coverage" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "34", - "display": "HRSA Program" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "341", - "display": "Title V (MCH Block Grant)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "342", - "display": "Migrant Health Program" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "343", - "display": "Ryan White Act" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "349", - "display": "Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "35", - "display": "Black Lung" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "36", - "display": "State Government" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "361", - "display": "State SCHIP program (codes for individual states)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "362", - "display": "Specific state programs (list/ local code)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "369", - "display": "State, not otherwise specified (other state)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "37", - "display": "Local Government" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "371", - "display": "Local - Managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3711", - "display": "HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3712", - "display": "PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3713", - "display": "POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "372", - "display": "FFS/Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "379", - "display": "Local, not otherwise specified (other local, county)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "38", - "display": "Other Government (Federal, State, Local not specified)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "381", - "display": "Federal, State, Local not specified managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3811", - "display": "Federal, State, Local not specified - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3812", - "display": "Federal, State, Local not specified - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3813", - "display": "Federal, State, Local not specified - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3819", - "display": "Federal, State, Local not specified - not specified managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "382", - "display": "Federal, State, Local not specified - FFS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "389", - "display": "Federal, State, Local not specified - Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "39", - "display": "Other Federal" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "391", - "display": "Federal Employee Health Plan - Use when known" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "4", - "display": "DEPARTMENTS OF CORRECTIONS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "41", - "display": "Corrections Federal" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "42", - "display": "Corrections State" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "43", - "display": "Corrections Local" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "44", - "display": "Corrections Unknown Level" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "5", - "display": "PRIVATE HEALTH INSURANCE" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "51", - "display": "Managed Care (Private)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "511", - "display": "Commercial Managed Care - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "512", - "display": "Commercial Managed Care - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "513", - "display": "Commercial Managed Care - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "514", - "display": "Exclusive Provider Organization" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "515", - "display": "Gatekeeper PPO (GPPO)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "516", - "display": "Commercial Managed Care - Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "517", - "display": "Commercial Managed Care - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "519", - "display": "Managed Care, Other (non HMO)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "52", - "display": "Private Health Insurance - Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "521", - "display": "Commercial Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "522", - "display": "Self-insured (ERISA) Administrative Services Only (ASO) plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "523", - "display": "Medicare supplemental policy (as second payer)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "524", - "display": "Indemnity Insurance - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "529", - "display": "Private health insurance--other commercial Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "53", - "display": "Managed Care (private) or private health insurance (indemnity), not otherwise specified" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "54", - "display": "Organized Delivery System" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "55", - "display": "Small Employer Purchasing Group" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "56", - "display": "Specialized Stand-Alone Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "561", - "display": "Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "562", - "display": "Vision" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "59", - "display": "Other Private Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "6", - "display": "BLUE CROSS/BLUE SHIELD" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "61", - "display": "BC Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "611", - "display": "BC Managed Care - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "612", - "display": "BC Managed Care - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "613", - "display": "BC Managed Care - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "614", - "display": "BC Managed Care - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "619", - "display": "BC Managed Care - Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "62", - "display": "BC Insurance Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "621", - "display": "BC Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "622", - "display": "BC Self-insured (ERISA) Administrative Services Only (ASO)Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "623", - "display": "BC Medicare Supplemental Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "629", - "display": "BC Indemnity - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "7", - "display": "MANAGED CARE, UNSPECIFIED (to be used only if one can't distinguish public from private)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "71", - "display": "HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "72", - "display": "PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "73", - "display": "POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "79", - "display": "Other Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "8", - "display": "NO PAYMENT from an Organization/Agency/Program/Private Payer Listed" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "81", - "display": "Self-pay (Includes applicants for insurance and Medicaid applicants)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "82", - "display": "No Charge" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "821", - "display": "Charity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "822", - "display": "Professional Courtesy" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "823", - "display": "Research/Clinical Trial" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "83", - "display": "Refusal to Pay/Bad Debt" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "84", - "display": "Hill Burton Free Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "85", - "display": "Research/Donor" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "89", - "display": "No Payment, Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "9", - "display": "MISCELLANEOUS/OTHER" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "91", - "display": "Foreign National" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "92", - "display": "Other (Non-government)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "93", - "display": "Disability Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "94", - "display": "Long-term Care Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "95", - "display": "Worker's Compensation" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "951", - "display": "Worker's Comp HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "953", - "display": "Worker's Comp Fee-for-Service" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "954", - "display": "Worker's Comp Other Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "959", - "display": "Worker's Comp, Other unspecified" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "96", - "display": "Auto Insurance (includes no fault)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "97", - "display": "Legal Liability / Liability Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "98", - "display": "Other specified but not otherwise classifiable (includes Hospice - Unspecified plan)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "99", - "display": "No Typology Code available for payment source" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "9999", - "display": "Unavailable / No Payer Specified / Blank" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.3591" - } - }, - { - "resource": { - "resourceType": "Condition", - "id": "CMSTest-patient-1-condition-1", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" - ] - }, - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "version": "0.5.0", - "code": "active", - "display": "Active" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "version": "0.5.0", - "code": "confirmed", - "display": "Confirmed" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/ValueSet/condition-category", - "version": "0.5.0", - "code": "encounter-diagnosis", - "display": "Encounter Diagnosis" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2021", - "code": "O30.833", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" - } - ] - }, - "subject": { - "reference": "Patient/CMSTest-patient-1" - }, - "onsetDateTime": "2023-01-05T10:00:00-07:00" - }, - "request": { - "method": "PUT", - "url": "Condition/CMSTest-patient-1-condition-1" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.836", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.836" - } - ], - "version": "20121025", - "name": "Race", - "title": "Race", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Race.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "1002-5", - "display": "American Indian or Alaska Native" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2028-9", - "display": "Asian" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2054-5", - "display": "Black or African American" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2076-8", - "display": "Native Hawaiian or Other Pacific Islander" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2106-3", - "display": "White" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2131-1", - "display": "Other Race" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.836" - } - }, - { - "resource": { - "resourceType": "Measure", - "id": "CMSTest", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" - ] - }, - "contained": [ - { - "resourceType": "Library", - "id": "effective-data-requirements", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "module-definition" - } - ] - }, - "relatedArtifact": [ - { - "type": "depends-on", - "display": "Library SDE", - "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - }, - { - "name": "Numerator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Denominator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Initial Population", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - } - ] - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - }, - { - "id": "effective-data-requirements", - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements", - "valueReference": { - "reference": "#effective-data-requirements" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Measure/CMSTest", - "name": "CMSTest", - "title": "Measure - CMSTest: test", - "status": "active", - "experimental": true, - "date": "2023-06-21T16:16:00-07:00", - "publisher": "Alphora", - "contact": [ - { - "telecom": [ - { - "system": "url", - "value": "https://alphora.com" - } - ] - } - ], - "description": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "version": "4.0.1", - "code": "program", - "display": "Program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } - ], - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", - "effectivePeriod": { - "start": "2020-01-01T00:00:00-07:00", - "end": "2020-12-31T23:59:59-07:00" - }, - "topic": [ - { - "coding": [ - { - "system": "http://loinc.org", - "code": "57024-2", - "display": "Health Quality Measure Document" - } - ] - } - ], - "library": [ - "http://content.alphora.com/fhir/dqm/Library/CMSTest" - ], - "scoring": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "version": "4.0.1", - "code": "proportion", - "display": "Proportion" - } - ] - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "version": "4.2.0", - "code": "process", - "display": "Process" - } - ] - } - ], - "rationale": "test.", - "clinicalRecommendationStatement": "test", - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "version": "0.1.0", - "code": "increase", - "display": "Increased score indicates improvement" - } - ] - }, - "group": [ - { - "id": "group-1", - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "denominator", - "display": "Denominator" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Denominator" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Numerator" - } - } - ] - } - ], - "supplementalData": [ - { - "id": "sde-sex", - "code": { - "text": "sde-sex" - }, - "usage": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-data-usage", - "code": "supplemental-data" - } - ] - } - ], - "criteria": { - "language": "text/cql.identifier", - "expression": "SDE Sex" - } - } - ] - }, - "request": { - "method": "PUT", - "url": "Measure/CMSTest" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "CMSTest", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/CMSTest", - "name": "CMSTest", - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FC", - "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon|4.0.1" - }, - { - "type": "depends-on", - "display": "Library SDE", - "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Code system ConditionClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" - }, - { - "type": "depends-on", - "display": "Code system ConditionVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" - }, - { - "type": "depends-on", - "display": "Value set Pregnancy", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - }, - { - "name": "Initial Population", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Denominator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Pregnant Patient", - "use": "out", - "min": 0, - "max": "*", - "type": "Condition" - }, - { - "name": "Numerator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBDTVNUZXN0Cgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkhJUkhlbHBlcnMKaW5jbHVkZSBGSElSQ29tbW9uIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkMKCmluY2x1ZGUgU0RFIGNhbGxlZCBTREUKdmFsdWVzZXQgIlByZWduYW5jeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMy0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIzLTEyLTMxVDAwOjAwOjAwLjAwMCkKCmNvbnRleHQgUGF0aWVudAoKZGVmaW5lICJTREUgU2V4IjoKICBTREUuIlNERSBTZXgiCgpkZWZpbmUgIkluaXRpYWwgUG9wdWxhdGlvbiI6CiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA+IDE4CiAgICAgICAgICAgIApkZWZpbmUgZnVuY3Rpb24gUXVhbGlmaWVkQ29uZGl0aW9ucyh2YWx1ZSBMaXN0PEZISVIuQ29uZGl0aW9uPik6CiAgdmFsdWUgQ29uZGl0aW9uCiAgICB3aGVyZSAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24uY2xpbmljYWxTdGF0dXMpIH4gRkMuImFjdGl2ZSIKICAgICkKICAgIGFuZCAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24udmVyaWZpY2F0aW9uU3RhdHVzKSB+IEZDLiJjb25maXJtZWQiCiAgICApCgpkZWZpbmUgIkRlbm9taW5hdG9yIjoKICAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA8IDk5CgoKZGVmaW5lICJOdW1lcmF0b3IiOgogIGV4aXN0cyAiUHJlZ25hbnQgUGF0aWVudCIKCmRlZmluZSAiUHJlZ25hbnQgUGF0aWVudCI6ICAgCiAgKFF1YWxpZmllZENvbmRpdGlvbnMoW0NvbmRpdGlvbjogIlByZWduYW5jeSJdKSkgUHJlZ25hbmN5CiB3aGVyZSBGQy5Ub1ByZXZhbGVuY2VJbnRlcnZhbChQcmVnbmFuY3kpIG92ZXJsYXBzICJNZWFzdXJlbWVudCBQZXJpb2QiICAgICAgICAgCiAgCgoKCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iQ01TVGVzdCIgc3lzdGVtPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiLz4KICAgPC91c2luZ3M+CiAgIDxpbmNsdWRlcz4KICAgICAgPGRlZiBsb2NhdG9yPSI0OjEtNDo1NCIgbG9jYWxJZGVudGlmaWVyPSJGSElSSGVscGVycyIgcGF0aD0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4xIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTU6NDQiIGxvY2FsSWRlbnRpZmllcj0iRkMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0ZISVJDb21tb24iIHZlcnNpb249IjQuMC4xIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzoxLTc6MjIiIGxvY2FsSWRlbnRpZmllcj0iU0RFIiBwYXRoPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9TREUiLz4KICAgPC9pbmNsdWRlcz4KICAgPHBhcmFtZXRlcnM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA6MS0xMDo5OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGRlZmF1bHQgbG9jYXRvcj0iMTA6NDAtMTA6OTkiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTA6NDktMTA6NzIiIHhzaTp0eXBlPSJEYXRlVGltZSI+CiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAyMyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1vbnRoIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbnV0ZSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTA6NzUtMTA6OTgiIHhzaTp0eXBlPSJEYXRlVGltZSI+CiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAyMyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1vbnRoIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMTIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIzMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPGhvdXIgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaWxsaXNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgPC9kZWZhdWx0PgogICAgICA8L2RlZj4KICAgPC9wYXJhbWV0ZXJzPgogICA8dmFsdWVTZXRzPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg6MS04OjkwIiBuYW1lPSJQcmVnbmFuY3kiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjoxLTEyOjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyOjEtMTI6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEyOjEtMTI6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDoxLTE1OjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU6My0xNToxNSIgbmFtZT0iU0RFIFNleCIgbGlicmFyeU5hbWU9IlNERSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc6MS0xODo1MCIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6My0xODo1MCIgeHNpOnR5cGU9IkdyZWF0ZXIiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODozLTE4OjQ1IiBwcmVjaXNpb249IlllYXIiIHhzaTp0eXBlPSJDYWxjdWxhdGVBZ2VBdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iYmlydGhEYXRlLnZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODoxNi0xODo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg6MjUtMTg6NDQiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg6NDktMTg6NTAiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMTgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA6MS0yNzo1IiBuYW1lPSJRdWFsaWZpZWRDb25kaXRpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTozLTI3OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6My0yMToxNyIgYWxpYXM9IkNvbmRpdGlvbiI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOjMtMjE6NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjI6NS0yNzo1IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI6MTEtMjQ6NSIgeHNpOnR5cGU9IkVxdWl2YWxlbnQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyMzo3LTIzOjUzIiBuYW1lPSJUb0NvbmNlcHQiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MjktMjM6NTIiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiBzY29wZT0iQ29uZGl0aW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iVG9Db25jZXB0Ij4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6NTctMjM6NjciIG5hbWU9ImFjdGl2ZSIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjU6OS0yNzo1IiB4c2k6dHlwZT0iRXF1aXZhbGVudCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjctMjY6NTciIG5hbWU9IlRvQ29uY2VwdCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyOS0yNjo1NiIgcGF0aD0idmVyaWZpY2F0aW9uU3RhdHVzIiBzY29wZT0iQ29uZGl0aW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iVG9Db25jZXB0Ij4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6NjEtMjY6NzQiIG5hbWU9ImNvbmZpcm1lZCIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDo0My0yMDo2MiIgeHNpOnR5cGU9Ikxpc3RUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgPGVsZW1lbnRUeXBlIGxvY2F0b3I9IjIwOjQ4LTIwOjYxIiBuYW1lPSJmaGlyOkNvbmRpdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICA8L29wZXJhbmRUeXBlU3BlY2lmaWVyPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzA6NTEiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA6NC0zMDo1MSIgeHNpOnR5cGU9Ikxlc3MiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDo0LTMwOjQ2IiBwcmVjaXNpb249IlllYXIiIHhzaTp0eXBlPSJDYWxjdWxhdGVBZ2VBdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iYmlydGhEYXRlLnZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDoxNy0zMDo0NSIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzA6MjYtMzA6NDUiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzA6NTAtMzA6NTEiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iOTkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY6MS0zODo3MSIgbmFtZT0iUHJlZ25hbnQgUGF0aWVudCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3OjMtMzg6NzEiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc6My0zNzo1OSIgYWxpYXM9IlByZWduYW5jeSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3OjMtMzc6NDkiIG5hbWU9IlF1YWxpZmllZENvbmRpdGlvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM3OjI0LTM3OjQ3IiBkYXRhVHlwZT0iZmhpcjpDb25kaXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db25kaXRpb24iIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzc6MzYtMzc6NDYiIG5hbWU9IlByZWduYW5jeSIgcHJlc2VydmU9InRydWUiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzODoyLTM4OjcxIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODo4LTM4OjQxIiBuYW1lPSJUb1ByZXZhbGVuY2VJbnRlcnZhbCIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODozMi0zODo0MCIgbmFtZT0iUHJlZ25hbmN5IiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODo1Mi0zODo3MSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM6MS0zNDoyNyIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ6My0zNDoyNyIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM0OjEwLTM0OjI3IiBuYW1lPSJQcmVnbmFudCBQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiQ01TVGVzdCIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOjEtMzoyNiIsCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVIiLAogICAgICAgICAgICAidXJpIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyIiwKICAgICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ6MS00OjU0IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MS01OjQ0IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkMiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkNvbW1vbiIsCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzoxLTc6MjIiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTREUiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vU0RFIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgInBhcmFtZXRlcnMiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA6MS0xMDo5OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJkZWZhdWx0IiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjQwLTEwOjk5IiwKICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDo0OS0xMDo3MiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ5ZWFyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDIzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZGF5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJob3VyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtaW51dGUiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInNlY29uZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWlsbGlzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDo3NS0xMDo5OCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ5ZWFyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDIzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMzEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhvdXIiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbnV0ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAic2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtaWxsaXNlY29uZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgInZhbHVlU2V0cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OjEtODo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFuY3kiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjM3OCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDoxLTE1OjE1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6My0xNToxNSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgU2V4IiwKICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTE4OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIkluaXRpYWwgUG9wdWxhdGlvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODozLTE4OjUwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkdyZWF0ZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjMtMTg6NDUiLAogICAgICAgICAgICAgICAgICAicHJlY2lzaW9uIiA6ICJZZWFyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNhbGN1bGF0ZUFnZUF0IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlLnZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxNi0xODo0NCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjI1LTE4OjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ5LTE4OjUwIiwKICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxOCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI3OjUiLAogICAgICAgICAgICAibmFtZSIgOiAiUXVhbGlmaWVkQ29uZGl0aW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI3OjUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yMToxNyIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yMTo3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjUtMjc6NSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjExLTI0OjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1aXZhbGVudCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6Ny0yMzo1MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzoyOS0yMzo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjbGluaWNhbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NTctMjM6NjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2RlUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTo5LTI3OjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1aXZhbGVudCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNjo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyOS0yNjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2ZXJpZmljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjYxLTI2Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmZpcm1lZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDo0My0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXN0VHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDo0OC0yMDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjEtMzA6NTEiLAogICAgICAgICAgICAibmFtZSIgOiAiRGVub21pbmF0b3IiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6NC0zMDo1MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMZXNzIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo0LTMwOjQ2IiwKICAgICAgICAgICAgICAgICAgInByZWNpc2lvbiIgOiAiWWVhciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYWxjdWxhdGVBZ2VBdCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZS52YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTctMzA6NDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiU3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDoyNi0zMDo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo1MC0zMDo1MSIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiOTkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6MS0zODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFudCBQYXRpZW50IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjMtMzg6NzEiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6My0zNzo1OSIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiUHJlZ25hbmN5IiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6My0zNzo0OSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJRdWFsaWZpZWRDb25kaXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyNC0zNzo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZVByb3BlcnR5IiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlcyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFuY3kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlc2VydmUiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoyLTM4OjcxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODo4LTM4OjQxIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUHJldmFsZW5jZUludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGQyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6MzItMzg6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUHJlZ25hbmN5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NTItMzg6NzEiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzoxLTM0OjI3IiwKICAgICAgICAgICAgIm5hbWUiIDogIk51bWVyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDozLTM0OjI3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjEwLTM0OjI3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlByZWduYW50IFBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9CiAgIH0KfQ==" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/CMSTest" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "FHIRHelpers", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers", - "version": "4.0.1", - "name": "FHIRHelpers", - "title": "Library - FHIR Helpers", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "Logic library used in mapping CQL logic with the R4 FHIR Data Model.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBUaGlzIGxpYnJhcnkgZGVmaW5lcyBmdW5jdGlvbnMgdG8gY29udmVydCBiZXR3ZWVuIEZISVIKIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydAogRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZToKIGh0dHBzOi8vZ2l0aHViLmNvbS9jcWZyYW1ld29yay9jbGluaWNhbF9xdWFsaXR5X2xhbmd1YWdlL3dpa2kvRkhJUkhlbHBlcnMKQGFsbG93Rmx1ZW50OiB0cnVlCiovCmxpYnJhcnkgRkhJUkhlbHBlcnMgdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocGVyaW9kIEZISVIuUGVyaW9kKToKICAgIGlmIHBlcmlvZCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgaWYgcGVyaW9kLiJzdGFydCIgaXMgbnVsbCB0aGVuCiAgICAgICAgICAgIEludGVydmFsKHBlcmlvZC4ic3RhcnQiLnZhbHVlLCBwZXJpb2QuImVuZCIudmFsdWVdCiAgICAgICAgZWxzZQogICAgICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ2FsZW5kYXJVbml0KHVuaXQgU3lzdGVtLlN0cmluZyk6CiAgICBjYXNlIHVuaXQKICAgICAgICB3aGVuICdtcycgdGhlbiAnbWlsbGlzZWNvbmQnCiAgICAgICAgd2hlbiAncycgdGhlbiAnc2Vjb25kJwogICAgICAgIHdoZW4gJ21pbicgdGhlbiAnbWludXRlJwogICAgICAgIHdoZW4gJ2gnIHRoZW4gJ2hvdXInCiAgICAgICAgd2hlbiAnZCcgdGhlbiAnZGF5JwogICAgICAgIHdoZW4gJ3drJyB0aGVuICd3ZWVrJwogICAgICAgIHdoZW4gJ21vJyB0aGVuICdtb250aCcKICAgICAgICB3aGVuICdhJyB0aGVuICd5ZWFyJwogICAgICAgIGVsc2UgdW5pdAogICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9RdWFudGl0eShxdWFudGl0eSBGSElSLlF1YW50aXR5KToKICAgIGNhc2UKICAgICAgICB3aGVuIHF1YW50aXR5IGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS52YWx1ZSBpcyBudWxsIHRoZW4gbnVsbAogICAgICAgIHdoZW4gcXVhbnRpdHkuY29tcGFyYXRvciBpcyBub3QgbnVsbCB0aGVuCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuQ29tcGFyYXRvclF1YW50aXR5Tm90U3VwcG9ydGVkJywgJ0Vycm9yJywgJ0ZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4nKQogICAgICAgIHdoZW4gcXVhbnRpdHkuc3lzdGVtIGlzIG51bGwgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly91bml0c29mbWVhc3VyZS5vcmcnCiAgICAgICAgICAgICAgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMnIHRoZW4KICAgICAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBUb0NhbGVuZGFyVW5pdChDb2FsZXNjZShxdWFudGl0eS5jb2RlLnZhbHVlLCBxdWFudGl0eS51bml0LnZhbHVlLCAnMScpKSB9CiAgICAgICAgZWxzZQogICAgICAgICAgICBNZXNzYWdlKG51bGwsIHRydWUsICdGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHknLCAnRXJyb3InLCAnSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICcgJiBxdWFudGl0eS51bml0LnZhbHVlICYgJyAoJyAmIHF1YW50aXR5LnN5c3RlbS52YWx1ZSAmICd8JyAmIHF1YW50aXR5LmNvZGUudmFsdWUgJiAnKScpCiAgICBlbmQKCmRlZmluZSBmdW5jdGlvbiBUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yKHF1YW50aXR5IEZISVIuUXVhbnRpdHkpOgogICAgY2FzZQogICAgICAgIHdoZW4gcXVhbnRpdHkgaXMgbnVsbCB0aGVuIG51bGwKICAgICAgICB3aGVuIHF1YW50aXR5LnZhbHVlIGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS5zeXN0ZW0gaXMgbnVsbCBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZycKICAgICAgICAgICAgICBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cycgdGhlbgogICAgICAgICAgICBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogcXVhbnRpdHkudmFsdWUudmFsdWUsIHVuaXQ6IFRvQ2FsZW5kYXJVbml0KENvYWxlc2NlKHF1YW50aXR5LmNvZGUudmFsdWUsIHF1YW50aXR5LnVuaXQudmFsdWUsICcxJykpIH0KICAgICAgICBlbHNlCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eScsICdFcnJvcicsICdJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogJyAmIHF1YW50aXR5LnVuaXQudmFsdWUgJiAnICgnICYgcXVhbnRpdHkuc3lzdGVtLnZhbHVlICYgJ3wnICYgcXVhbnRpdHkuY29kZS52YWx1ZSAmICcpJykKICAgIGVuZAoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4gbnVsbCBlbHNlCiAgICAgICAgY2FzZSBxdWFudGl0eS5jb21wYXJhdG9yLnZhbHVlCiAgICAgICAgICAgIHdoZW4gJzwnIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgd2hlbiAnPD0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPj0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIFRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IocXVhbnRpdHkpLAogICAgICAgICAgICAgICAgICAgIG51bGwKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPicgdGhlbgogICAgICAgICAgICAgICAgSW50ZXJ2YWwoCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSksCiAgICAgICAgICAgICAgICAgICAgbnVsbAogICAgICAgICAgICAgICAgXQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBJbnRlcnZhbFtUb1F1YW50aXR5KHF1YW50aXR5KSwgVG9RdWFudGl0eShxdWFudGl0eSldCiAgICAgICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9SYXRpbyhyYXRpbyBGSElSLlJhdGlvKToKICAgIGlmIHJhdGlvIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uUmF0aW8geyBudW1lcmF0b3I6IFRvUXVhbnRpdHkocmF0aW8ubnVtZXJhdG9yKSwgZGVub21pbmF0b3I6IFRvUXVhbnRpdHkocmF0aW8uZGVub21pbmF0b3IpIH0KCmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKHJhbmdlIEZISVIuUmFuZ2UpOgogICAgaWYgcmFuZ2UgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIEludGVydmFsW1RvUXVhbnRpdHkocmFuZ2UubG93KSwgVG9RdWFudGl0eShyYW5nZS5oaWdoKV0KCmRlZmluZSBmdW5jdGlvbiBUb0NvZGUoY29kaW5nIEZISVIuQ29kaW5nKToKICAgIGlmIGNvZGluZyBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvZGUgewogICAgICAgICAgY29kZTogY29kaW5nLmNvZGUudmFsdWUsCiAgICAgICAgICBzeXN0ZW06IGNvZGluZy5zeXN0ZW0udmFsdWUsCiAgICAgICAgICB2ZXJzaW9uOiBjb2RpbmcudmVyc2lvbi52YWx1ZSwKICAgICAgICAgIGRpc3BsYXk6IGNvZGluZy5kaXNwbGF5LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29uY2VwdChjb25jZXB0IEZISVIuQ29kZWFibGVDb25jZXB0KToKICAgIGlmIGNvbmNlcHQgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5Db25jZXB0IHsKICAgICAgICAgICAgY29kZXM6IGNvbmNlcHQuY29kaW5nIEMgcmV0dXJuIFRvQ29kZShDKSwKICAgICAgICAgICAgZGlzcGxheTogY29uY2VwdC50ZXh0LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZWZlcmVuY2UgU3RyaW5nKToKICAgIGlmIHJlZmVyZW5jZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgUmVmZXJlbmNlIHsgcmVmZXJlbmNlOiBzdHJpbmcgeyB2YWx1ZTogcmVmZXJlbmNlIH0gfQoKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFN0cmluZykgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFJlZmVyZW5jZSkgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZXNvdXJjZSBSZXNvdXJjZSkgcmV0dXJucyBSZWZlcmVuY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24oZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nKSByZXR1cm5zIExpc3Q8RWxlbWVudD46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24ocmVzb3VyY2UgUmVzb3VyY2UsIHVybCBTdHJpbmcpIHJldHVybnMgTGlzdDxFbGVtZW50PjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIGhhc1ZhbHVlKGVsZW1lbnQgRWxlbWVudCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZ2V0VmFsdWUoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEFueTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG9mVHlwZShpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBMaXN0PEFueT46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBpcyhpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gYXMoaWRlbnRpZmllciBTdHJpbmcpIHJldHVybnMgQW55OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZWxlbWVudERlZmluaXRpb24oZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnREZWZpbml0aW9uOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc2xpY2UoZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nLCBuYW1lIFN0cmluZykgcmV0dXJucyBMaXN0PEVsZW1lbnQ+OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMocmVzb3VyY2UgUmVzb3VyY2UpIHJldHVybnMgUmVzb3VyY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhyZXNvdXJjZSBSZXNvdXJjZSwgbW9kaWZpZXIgU3RyaW5nKSByZXR1cm5zIFJlc291cmNlOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnQ6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhlbGVtZW50IEVsZW1lbnQsIG1vZGlmaWVyIFN0cmluZykgcmV0dXJucyBFbGVtZW50OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY29uZm9ybXNUbyhyZXNvdXJjZSBSZXNvdXJjZSwgc3RydWN0dXJlIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29kZSBjb2RlLCB2YWx1ZVNldCBTdHJpbmcpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG1lbWJlck9mKGNvZGluZyBDb2RpbmcsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29uY2VwdCBDb2RlYWJsZUNvbmNlcHQsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZXMoY29kaW5nIENvZGluZywgc3Vic3VtZWRDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lcyhjb25jZXB0IENvZGVhYmxlQ29uY2VwdCwgc3Vic3VtZWRDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZWRCeShjb2RpbmcgQ29kaW5nLCBzdWJzdW1pbmdDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lZEJ5KGNvbmNlcHQgQ29kZWFibGVDb25jZXB0LCBzdWJzdW1pbmdDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gaHRtbENoZWNrcyhlbGVtZW50IEVsZW1lbnQpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY2NvdW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ29uZGl0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Hcm91cGluZ0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25QcmVjaGVja0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVxdWlyZWRCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25TZWxlY3Rpb25CZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpdml0eURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRtaW5pc3RyYXRpdmVHZW5kZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWR2ZXJzZUV2ZW50QWN0dWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFnZ3JlZ2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFwcG9pbnRtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvbkRpcmVjdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uT3BlcmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvblJlc3BvbnNlVHlwZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXVkaXRFdmVudEFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50T3V0Y29tZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW5kaW5nU3RyZW5ndGgpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCdW5kbGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5JbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVRlYW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2xhaW1SZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGluaWNhbEltcHJlc3Npb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVNlYXJjaFN1cHBvcnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUNvbnRlbnRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25Qcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wYXJ0bWVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEVxdWl2YWxlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbERlbGV0ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbFJlYWRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudERhdGFNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRQcm92aXNpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRTdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zdHJhaW50U2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50U3lzdGVtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRhY3RQb2ludFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250cmFjdFB1YmxpY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyaWJ1dG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb3ZlcmFnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDdXJyZW5jeUNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5T2ZXZWVrKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERheXNPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldGVjdGVkSXNzdWVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ29sb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTmFtZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGlhZ25vc3RpY1JlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaXNjcmltaW5hdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudENvbmZpZGVudGlhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5hYmxlV2hlbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlckxvY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmRwb2ludFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXBpc29kZU9mQ2FyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFdmVudENhcGFiaWxpdHlNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50VGltaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2aWRlbmNlVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBvc3VyZVN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4dGVuc2lvbkNvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJBbGxUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGVmaW5lZFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkRldmljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSUmVzb3VyY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJTdWJzdGFuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlZlcnNpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRmFtaWx5SGlzdG9yeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGaWx0ZXJPcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGbGFnU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdvYWxMaWZlY3ljbGVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFJ1bGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cE1lYXN1cmUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JvdXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRhbmNlUmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGVQYWdlR2VuZXJhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhcmFtZXRlckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSFRUUFZlcmIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSWRlbnRpZmllclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGl0eUFzc3VyYW5jZUxldmVsKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltYWdpbmdTdHVkeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSXNzdWVTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVhc3VyZVJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpYVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNaW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYW1lVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFycmF0aXZlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5vdGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlpb25PcmRlckludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOdXRyaXRpb25PcmRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvbkRhdGFUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcGVyYXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvblBhcmFtZXRlclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcmllbnRhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBhcnRpY2lwYW50UmVxdWlyZWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXltZW50Tm90aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9jZWR1cmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvcGVydHlSZXByZXNlbnRhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvdmVuYW5jZUVudGl0eVJvbGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbGl0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbnRpdHlDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VIYW5kbGluZ1BvbGljeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VWZXJzaW9uUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVmZXJyZWREb2N1bWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWxhdGVkQXJ0aWZhY3RUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbWl0dGFuY2VPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcG9zaXRvcnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hFbGVtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaFN0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3ViamVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzb3VyY2VWZXJzaW9uUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3BvbnNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXN0ZnVsQ2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmlza0Fzc2Vzc21lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU1BEWExpY2Vuc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoQ29tcGFyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hFbnRyeU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoTW9kaWZpZXJDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaFBhcmFtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWN0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXF1ZW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbGljaW5nUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2xvdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTb3J0RGlyZWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTcGVjaW1lblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RyYW5kVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcElucHV0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVHJhbnNmb3JtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlEZWxpdmVyeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrSW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRBY3Rpb25SZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UmVzdWx0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRyaWdnZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVEZXJpdmF0aW9uUnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUeXBlUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVESUVudHJ5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVbml0c09mVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvbkJhc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uRXllcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgWFBhdGhVc2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgYmFzZTY0QmluYXJ5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvQm9vbGVhbih2YWx1ZSBib29sZWFuKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZSh2YWx1ZSBkYXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgZGF0ZVRpbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EZWNpbWFsKHZhbHVlIGRlY2ltYWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBpbnN0YW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZWdlcih2YWx1ZSBpbnRlZ2VyKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHN0cmluZyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1RpbWUodmFsdWUgdGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB1cmkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgeGh0bWwpOiB2YWx1ZS52YWx1ZQo=" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iVGhpcyBsaWJyYXJ5IGRlZmluZXMgZnVuY3Rpb25zIHRvIGNvbnZlcnQgYmV0d2VlbiBGSElSJiN4YTsgZGF0YSB0eXBlcyBhbmQgQ1FMIHN5c3RlbS1kZWZpbmVkIHR5cGVzLCBhcyB3ZWxsIGFzIGZ1bmN0aW9ucyB0byBzdXBwb3J0JiN4YTsgRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZTomI3hhOyBodHRwczovL2dpdGh1Yi5jb20vY3FmcmFtZXdvcmsvY2xpbmljYWxfcXVhbGl0eV9sYW5ndWFnZS93aWtpL0ZISVJIZWxwZXJzIi8+CiAgICAgIDxhOnQgbmFtZT0iYWxsb3dGbHVlbnQiIHZhbHVlPSJ0cnVlIi8+CiAgIDwvYW5ub3RhdGlvbj4KICAgPGlkZW50aWZpZXIgaWQ9IkZISVJIZWxwZXJzIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOjEtMTE6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0yMDo2MiIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ6NS0yMDo2MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDo4LTE0OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ6OC0xNDoxMyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTU6OS0xNToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE3OjktMjA6NjIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTc6MTItMTc6MzMiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNzoxMi0xNzoyNSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc6MTItMTc6MTciIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTg6MTMtMTg6NjIiIGxvd0Nsb3NlZD0iZmFsc2UiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTg6MjItMTg6NDEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjIyLTE4OjM1IiBwYXRoPSJzdGFydCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODoyMi0xODoyNyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE4OjQ0LTE4OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODo0NC0xODo1NSIgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjQ0LTE4OjQ5IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjA6MTMtMjA6NjIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIyMDoyMi0yMDo0MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6MjItMjA6MzUiIHBhdGg9InN0YXJ0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjIyLTIwOjI3IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjA6NDQtMjA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjQ0LTIwOjU1IiBwYXRoPSJlbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6NDQtMjA6NDkiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icGVyaW9kIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOjM1LTEzOjQ1IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyOjEtMzM6NyIgbmFtZT0iVG9DYWxlbmRhclVuaXQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOjUtMzM6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y29tcGFyYW5kIGxvY2F0b3I9IjIzOjEwLTIzOjEzIiBuYW1lPSJ1bml0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMjQ6OS0yNDozNiI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjI0OjE0LTI0OjE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibXMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjI0OjI0LTI0OjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibWlsbGlzZWNvbmQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNTo5LTI1OjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjU6MTQtMjU6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJzIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNToyMy0yNTozMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InNlY29uZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI2OjktMjY6MzIiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyNjoxNC0yNjoxOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im1pbiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMjY6MjUtMjY6MzIiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtaW51dGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNzo5LTI3OjI4Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjc6MTQtMjc6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJoIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNzoyMy0yNzoyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImhvdXIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyODo5LTI4OjI3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjg6MTQtMjg6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyODoyMy0yODoyNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImRheSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI5OjktMjk6MjkiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyOToxNC0yOToxNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndrIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyOToyNC0yOToyOSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndlZWsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzMDo5LTMwOjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMzA6MTQtMzA6MTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtbyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMzA6MjQtMzA6MzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtb250aCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjMxOjktMzE6MjgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzMToxNC0zMToxNiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjMxOjIzLTMxOjI4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ieWVhciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMzI6MTQtMzI6MTciIG5hbWU9InVuaXQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjozNy0yMjo0OSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTQ2OjciIG5hbWU9IlRvUXVhbnRpdHkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2OjUtNDY6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMzc6OS0zNzozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM3OjE0LTM3OjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MTQtMzc6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MzYtMzc6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjM4OjktMzg6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzODoxNC0zODozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjE0LTM4OjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODoxNC0zODoyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjQyLTM4OjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzOTo5LTQwOjE4OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM5OjE0LTM5OjQ0IiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzk6MTQtMzk6NDQiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzOToxNC0zOTozMiIgcGF0aD0iY29tcGFyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOToxNC0zOToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTMtNDA6MTg5IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA6MjEtNDA6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDA6MjctNDA6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNDA6MzMtNDA6ODciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkNvbXBhcmF0b3JRdWFudGl0eU5vdFN1cHBvcnRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9IjQwOjkwLTQwOjk2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjQwOjk5LTQwOjE4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNDE6OS00MzoxMzgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI0MToxNC00Mjo5MiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6OTEiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjE0LTQxOjM2IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6MjgiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MToxNC00MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo0MS00MTo5MSIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6NDEtNDE6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQxOjQxLTQxOjU1IiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDE6NDEtNDE6NDgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo2NS00MTo5MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxOC00Mjo5MiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6MTgtNDI6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQyOjE4LTQyOjMyIiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDI6MTgtNDI6MjUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mjo0Mi00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI0MzoxMy00MzoxMzgiIGNsYXNzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6MzgtNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjM4LTQzOjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MzozOC00Mzo0NSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InVuaXQiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6NjYtNDM6MTM2IiBuYW1lPSJUb0NhbGVuZGFyVW5pdCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDM6ODEtNDM6MTM1IiB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mzo5MC00MzoxMDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjkwLTQzOjEwMiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0Mzo5MC00Mzo5NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjExMS00MzoxMjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjExMS00MzoxMjMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDM6MTExLTQzOjExOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjEzMi00MzoxMzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8ZWxzZSBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjIwNSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjIxLTQ1OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjQ1OjI3LTQ1OjMwIiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9IjQ1OjMzLTQ1Ojc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5JbnZhbGlkRkhJUlF1YW50aXR5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8c2V2ZXJpdHkgbG9jYXRvcj0iNDU6NzktNDU6ODUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJFcnJvciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG1lc3NhZ2UgbG9jYXRvcj0iNDU6ODgtNDU6MjA0IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxOTgiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjE3NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTcwIiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxNDYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjEzOSIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxMjEtNDU6MTM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxMjEtNDU6MTMzIiBwYXRoPSJ1bml0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjEyMS00NToxMjgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxNDMtNDU6MTQ2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iICgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE1MC00NToxNzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE1MC00NToxNjQiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxNTAtNDU6MTU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTc0LTQ1OjE3NiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE4MC00NToxOTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE4MC00NToxOTIiIHBhdGg9ImNvZGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDU6MTgwLTQ1OjE4NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjIwMi00NToyMDQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIpIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbWVzc2FnZT4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU6MzctMzU6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTU3OjciIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQ5OjUtNTc6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTA6OS01MDozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUwOjE0LTUwOjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MTQtNTA6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MzYtNTA6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjUxOjktNTE6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MToxNC01MTozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjE0LTUxOjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MToxNC01MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjQyLTUxOjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1Mjo5LTU0OjEzOCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUyOjE0LTUzOjkyIiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01Mjo5MSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTQtNTI6MzYiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01MjoyOCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUyOjE0LTUyOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjQxLTUyOjkxIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Mjo0MS01Mjo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTI6NDEtNTI6NTUiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo0MS01Mjo0OCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjY1LTUyOjkxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjE4LTUzOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxOC01MzozOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6MTgtNTM6MzIiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MzoxOC01MzoyNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjQyLTUzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjU0OjEzLTU0OjEzOCIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDozOC01NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MzgtNTQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjM4LTU0OjQ1IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDo2Ni01NDoxMzYiIG5hbWU9IlRvQ2FsZW5kYXJVbml0IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NDo4MS01NDoxMzUiIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU0OjkwLTU0OjEwOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6OTAtNTQ6MTAyIiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjkwLTU0Ojk3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTExLTU0OjEyOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MTExLTU0OjEyMyIgcGF0aD0idW5pdCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NDoxMTEtNTQ6MTE4IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTMyLTU0OjEzNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTMtNTY6MjA1IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MjEtNTY6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNTY6MjctNTY6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNTY6MzMtNTY6NzYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSI1Njo3OS01Njo4NSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkVycm9yIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8bWVzc2FnZSBsb2NhdG9yPSI1Njo4OC01NjoyMDQiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE5OCIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTc2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxNzAiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE0NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTM5IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxMTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjEyMS01NjoxMzkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjEyMS01NjoxMzMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTIxLTU2OjEyOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjE0My01NjoxNDYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIgKCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTUwLTU2OjE3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTUwLTU2OjE2NCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjE1MC01NjoxNTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NjoxNzQtNTY6MTc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ifCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTgwLTU2OjE5OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTgwLTU2OjE5MiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NjoxODAtNTY6MTg3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MjAyLTU2OjIwNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IikiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9tZXNzYWdlPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0ODo1NS00ODo2NyIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtODQ6MTEiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYwOjUtODQ6MTEiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNjA6OC02MDoyMyIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjYwOjgtNjA6MTUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjA6MzAtNjA6MzMiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI2MTo5LTg0OjExIiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgICAgIDxjb21wYXJhbmQgbG9jYXRvcj0iNjE6MTQtNjE6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjMyIiBwYXRoPSJjb21wYXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvY29tcGFyYW5kPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNjI6MTMtNjY6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI2MjoxOC02MjoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiZsdDsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjYzOjE3LTY2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjQ6MjEtNjQ6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI2NToyMS02NTo1OCIgbmFtZT0iVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjU6NTAtNjU6NTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjY3OjEzLTcxOjE3Ij4KICAgICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iNjc6MTgtNjc6MjEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSImbHQ7PSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iNjg6MTctNzE6MTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgPGxvdyBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY5OjIxLTY5OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iNzA6MjEtNzA6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjcwOjUwLTcwOjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI3MjoxMy03NjoxNyI+CiAgICAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjcyOjE4LTcyOjIxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iPj0iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjczOjE3LTc2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iNzQ6MjEtNzQ6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc0OjUwLTc0OjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzU6MjEtNzU6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNzc6MTMtODE6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI3NzoxOC03NzoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ij4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9Ijc4OjE3LTgxOjE3IiBsb3dDbG9zZWQ9ImZhbHNlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijc5OjIxLTc5OjU4IiBuYW1lPSJUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3OTo1MC03OTo1NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgwOjIxLTgwOjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iODM6MTctODM6NjgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MzoyNi04Mzo0NSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODM6MzctODM6NDQiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODM6NDgtODM6NjciIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjU5LTgzOjY2IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OTozNy01OTo0OSIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg2OjEtOTA6MTA3IiBuYW1lPSJUb1JhdGlvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo1LTkwOjEwNyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSI4Nzo4LTg3OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODc6OC04NzoxMiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjktODg6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTA6OS05MDoxMDciIGNsYXNzVHlwZT0idDpSYXRpbyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ibnVtZXJhdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjM1LTkwOjYxIiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo0Ni05MDo2MCIgcGF0aD0ibnVtZXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjQ2LTkwOjUwIiBuYW1lPSJyYXRpbyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRlbm9taW5hdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjc3LTkwOjEwNSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTA6ODgtOTA6MTA0IiBwYXRoPSJkZW5vbWluYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MDo4OC05MDo5MiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmF0aW8iPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODY6MzEtODY6NDAiIG5hbWU9ImZoaXI6UmF0aW8iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MjoxLTk2OjYzIiBuYW1lPSJUb0ludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mzo1LTk2OjYzIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjkzOjgtOTM6MjAiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mzo4LTkzOjEyIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjktOTQ6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI5Njo5LTk2OjYzIiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTY6MTgtOTY6MzgiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk2OjI5LTk2OjM3IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6MjktOTY6MzMiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Njo0MS05Njo2MiIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTY6NTItOTY6NjEiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6NTItOTY6NTYiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhbmdlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjM0LTkyOjQzIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTg6MS0xMDc6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5OTo1LTEwNzo5IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk5OjgtOTk6MjEiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5OTo4LTk5OjEzIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjktMTAwOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjEwMjo5LTEwNzo5IiBjbGFzc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iY29kZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxMDM6MTctMTAzOjMzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDM6MTctMTAzOjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMzoxNy0xMDM6MjIiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ic3lzdGVtIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDQ6MTktMTA0OjI0IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InZlcnNpb24iPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA1OjIwLTEwNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjIwLTEwNTozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDU6MjAtMTA1OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA2OjIwLTEwNjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA2OjIwLTEwNjozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6MjAtMTA2OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTg6MzEtOTg6NDEiIG5hbWU9ImZoaXI6Q29kaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA5OjEtMTE2OjkiIG5hbWU9IlRvQ29uY2VwdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEwOjUtMTE2OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEwOjgtMTEwOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEwOjgtMTEwOjE0IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExMTo5LTExMToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxMTM6OS0xMTY6OSIgY2xhc3NUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGVzIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjExNDoyMC0xMTQ6NTIiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE0OjIwLTExNDozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTQ6MjAtMTE0OjMzIiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iY29uY2VwdCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxMTQ6MzctMTE0OjUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTE0OjQ0LTExNDo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTQ6NTEiIG5hbWU9IkMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTE1OjIyLTExNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE1OjIyLTExNTozMyIgcGF0aD0idGV4dCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTU6MjItMTE1OjI4IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDk6MzUtMTA5OjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExODoxLTEyMjo2MCIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTk6NS0xMjI6NjAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTE5OjgtMTE5OjI0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE5OjgtMTE5OjE2IiBuYW1lPSJyZWZlcmVuY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMjA6OS0xMjA6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTIyOjktMTIyOjYwIiBjbGFzc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJyZWZlcmVuY2UiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjMyLTEyMjo1OCIgY2xhc3NUeXBlPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjQ4LTEyMjo1NiIgbmFtZT0icmVmZXJlbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzctMTE4OjQyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNDoxLTEyNDo2OCIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzUtMTI0OjQwIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo3MSIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjU6MzUtMTI1OjQzIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNjoxLTEyNjo3MiIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI2OjM2LTEyNjo0MyIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNzoxLTEyNzo4NiIgbmFtZT0iZXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjc6MzUtMTI3OjQxIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjQ4LTEyNzo1MyIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjg6MS0xMjg6ODgiIG5hbWU9ImV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozNi0xMjg6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI4OjUwLTEyODo1NSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjciIG5hbWU9Imhhc1ZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzQtMTI5OjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjMiIG5hbWU9ImdldFZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzQtMTMwOjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzE6MS0xMzE6NjkiIG5hbWU9Im9mVHlwZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkZW50aWZpZXIiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTMxOjM1LTEzMTo0MCIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzI6MS0xMzI6NjMiIG5hbWU9ImlzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWRlbnRpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzI6MzEtMTMyOjM2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMzoxLTEzMzo1OSIgbmFtZT0iYXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZGVudGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMzozMS0xMzM6MzYiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0Ojg2IiBuYW1lPSJlbGVtZW50RGVmaW5pdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjQzLTEzNDo0OSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM1OjEtMTM1Ojk1IiBuYW1lPSJzbGljZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM1OjMxLTEzNTozNyIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0NC0xMzU6NDkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ibmFtZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6NTctMTM1OjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNjoxLTEzNjo3NiIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzY6NDEtMTM2OjQ4IiBuYW1lPSJmaGlyOlJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM3OjEtMTM3OjkzIiBuYW1lPSJjaGVja01vZGlmaWVycyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNzo0MS0xMzc6NDgiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJtb2RpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6NjAtMTM3OjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzODoxLTEzODo3MyIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzODo0MC0xMzg6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo5MCIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo0MC0xMzk6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9Im1vZGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo1OC0xMzk6NjMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQwOjEtMTQwOjg5IiBuYW1lPSJjb25mb3Jtc1RvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQwOjM3LTE0MDo0NCIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN0cnVjdHVyZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6NTctMTQwOjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MToxLTE0MTo3OCIgbmFtZT0ibWVtYmVyT2YiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTozMS0xNDE6MzQiIG5hbWU9ImZoaXI6Y29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTo0Ni0xNDE6NTEiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQyOjEtMTQyOjgyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDI6MzMtMTQyOjM4IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Mjo1MC0xNDI6NTUiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQzOjEtMTQzOjkyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmNlcHQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjM0LTE0Mzo0OCIgbmFtZT0iZmhpcjpDb2RlYWJsZUNvbmNlcHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZVNldCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6NjAtMTQzOjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo4OCIgbmFtZT0ic3Vic3VtZXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDozOCIgbmFtZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJzdWJzdW1lZENvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDQ6NTYtMTQ0OjYxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NToxLTE0NToxMDgiIG5hbWU9InN1YnN1bWVzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzQtMTQ1OjQ4IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWVkQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6NjctMTQ1OjgxIiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NjoxLTE0Njo5MSIgbmFtZT0ic3Vic3VtZWRCeSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6MzUtMTQ2OjQwIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWluZ0NvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6NTktMTQ2OjY0IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NzoxLTE0NzoxMTEiIG5hbWU9InN1YnN1bWVkQnkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NzozNi0xNDc6NTAiIG5hbWU9ImZoaXI6Q29kZWFibGVDb25jZXB0IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ic3Vic3VtaW5nQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6NzAtMTQ3Ojg0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0ODoxLTE0ODo2OSIgbmFtZT0iaHRtbENoZWNrcyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjM2LTE0ODo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjQ4LTE1MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQ4LTE1MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo0NCIgbmFtZT0iZmhpcjpBY2NvdW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUxOjEtMTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUxOjYwLTE1MTo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjYwLTE1MTo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjMyLTE1MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjU0LTE1Mjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjU0LTE1Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1MCIgbmFtZT0iZmhpcjpBY3Rpb25Db25kaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUzOjEtMTUzOjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUzOjU3LTE1Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUzOjU3LTE1Mzo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUzOjMyLTE1Mzo1MyIgbmFtZT0iZmhpcjpBY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU0OjEtMTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU0OjU2LTE1NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU0OjU2LTE1NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU0OjMyLTE1NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NTctMTU1OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NTctMTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjUzIiBuYW1lPSJmaGlyOkFjdGlvblByZWNoZWNrQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTY6MS0xNTY6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTY6NTctMTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTY6NTctMTU2OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTY6MzItMTU2OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTc6MS0xNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTc6NTctMTU3OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTc6NTctMTU3OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTc6MzItMTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTg6MS0xNTg6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTg6NTgtMTU4OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTg6NTgtMTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTg6MzItMTU4OjU0IiBuYW1lPSJmaGlyOkFjdGlvblNlbGVjdGlvbkJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU3LTE1OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU3LTE1OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MyIgbmFtZT0iZmhpcjpBY3Rpdml0eURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjU4LTE2MDo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjU4LTE2MDo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NDIiIG5hbWU9ImZoaXI6QWRkcmVzc1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjI6MS0xNjI6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6NDUtMTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjI6NDUtMTYyOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjI6MzItMTYyOjQxIiBuYW1lPSJmaGlyOkFkZHJlc3NVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NTUtMTYzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NTUtMTYzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY0OjEtMTY0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY0OjU2LTE2NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY0OjU2LTE2NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY0OjMyLTE2NDo1MiIgbmFtZT0iZmhpcjpBZHZlcnNlRXZlbnRBY3R1YWxpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTAtMTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTAtMTY1OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjQ2IiBuYW1lPSJmaGlyOkFnZ3JlZ2F0aW9uTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2NjoxLTE2Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Njo2MS0xNjY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Njo2MS0xNjY6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2NjozMi0xNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjc6MS0xNjc6NzQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjc6NjQtMTY3Ojc0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjc6NjQtMTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjc6MzItMTY3OjYwIiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY4OjEtMTY4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY4OjYxLTE2ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY4OjYxLTE2ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjMyLTE2ODo1NyIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NDgiIG5hbWU9ImZoaXI6QXBwb2ludG1lbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzE6MS0xNzE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzE6NTctMTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzE6NTctMTcxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzE6MzItMTcxOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvbkRpcmVjdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzI6MS0xNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzI6NTYtMTcyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzI6NTYtMTcyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzI6MzItMTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NTUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NDciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo2MS0xNzU6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo2MS0xNzU6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzY6MS0xNzY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzY6NTItMTc2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzY6NTItMTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzY6MzItMTc2OjQ4IiBuYW1lPSJmaGlyOkF1ZGl0RXZlbnRPdXRjb21lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUwLTE3Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUwLTE3Nzo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0NiIgbmFtZT0iZmhpcjpCaW5kaW5nU3RyZW5ndGgiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjktMTc4Ojc5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjktMTc4OjczIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NzciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NjctMTc5Ojc3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NjctMTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjYzIiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTgwOjEtMTgwOjgzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTgwOjczLTE4MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTgwOjczLTE4MDo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTgwOjMyLTE4MDo2OSIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MToxLTE4MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4MTo0NS0xODE6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4MTo0NS0xODE6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MTozMi0xODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6NTQiIG5hbWU9ImZoaXI6Q2FwYWJpbGl0eVN0YXRlbWVudEtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NTUtMTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NTUtMTgzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjUxIiBuYW1lPSJmaGlyOkNhcmVQbGFuQWN0aXZpdHlLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjU3LTE4NDo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjU3LTE4NDo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg1OjEtMTg1OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg1OjQ5LTE4NTo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg1OjQ5LTE4NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg1OjMyLTE4NTo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhbkludGVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0OS0xODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0OS0xODY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODc6MS0xODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODc6NDktMTg3OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODc6NDktMTg3OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODc6MzItMTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg4OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg4OjU5LTE4ODo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg4OjU5LTE4ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjMyLTE4ODo1NSIgbmFtZT0iZmhpcjpDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODk6MS0xODk6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODk6NzMtMTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODk6NzMtMTg5Ojc3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODk6MzItMTg5OjY5IiBuYW1lPSJmaGlyOkNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkwOjEtMTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkwOjUxLTE5MDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkwOjUxLTE5MDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkwOjMyLTE5MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjU0LTE5MTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjU0LTE5MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo1MCIgbmFtZT0iZmhpcjpDbGFpbVJlc3BvbnNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkyOjEtMTkyOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkyOjMyLTE5Mjo0MiIgbmFtZT0iZmhpcjpDbGFpbVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk0OjEtMTk0OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk0OjUyLTE5NDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk0OjUyLTE5NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk0OjMyLTE5NDo0OCIgbmFtZT0iZmhpcjpDb2RlU2VhcmNoU3VwcG9ydCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NToxLTE5NTo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NTozMi0xOTU6NTIiIG5hbWU9ImZoaXI6Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk2OjEtMTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk2OjYxLTE5Njo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk2OjYxLTE5Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjMyLTE5Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NzoxLTE5Nzo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NzozMi0xOTc6NTIiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjYxLTE5ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjYxLTE5ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo1NyIgbmFtZT0iZmhpcjpDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5OToxLTE5OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OTo1NC0xOTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5OTo1NC0xOTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5OTozMi0xOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMDoxLTIwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMDo1MC0yMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMDo1MC0yMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMDozMi0yMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAxOjEtMjAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAxOjUwLTIwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAxOjUwLTIwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAxOjMyLTIwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NjEtMjAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NjEtMjAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAzOjEtMjAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAzOjUyLTIwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAzOjUyLTIwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAzOjMyLTIwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwNDoxLTIwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNDozMi0yMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjYyLTIwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjYyLTIwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NTgtMjA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NTgtMjA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTMtMjA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTMtMjA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOToxLTIwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwOTo1NS0yMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOTo1NS0yMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwOTozMi0yMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTA6MS0yMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTA6NDctMjEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTA6NDctMjEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTA6MzItMjEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxMToxLTIxMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxMTo1My0yMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxMTo1My0yMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxMTozMi0yMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjUzLTIxMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjUzLTIxMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTM6MS0yMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTM6NTAtMjEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTM6NTAtMjEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTM6MzItMjEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNDoxLTIxNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNDozMi0yMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTY6MS0yMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTY6NTAtMjE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6NTAtMjE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTY6MzItMjE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNzoxLTIxNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNzozMi0yMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTg6MS0yMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTg6NDctMjE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTg6NDctMjE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTg6MzItMjE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo0NC0yMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo0NC0yMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIwOjEtMjIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIwOjQ1LTIyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIwOjQ1LTIyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIwOjMyLTIyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjU2LTIyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjU2LTIyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTQtMjIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTQtMjIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NjMtMjIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NjMtMjIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjQ6MS0yMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjQ6NjItMjI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjQ6NjItMjI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjQ6MzItMjI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNToxLTIyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNTo1NS0yMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNTo1NS0yMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNTozMi0yMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjY6MS0yMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6NTItMjI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjY6NTItMjI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjY6MzItMjI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI3OjEtMjI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI3OjY0LTIyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI3OjY0LTIyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI3OjMyLTIyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyODoxLTIyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyODo0OS0yMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyODo0OS0yMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyODozMi0yMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjk6MS0yMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjk6NTQtMjI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjk6NTQtMjI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjk6MzItMjI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NTktMjMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NTktMjMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMjoxLTIzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMjozMi0yMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzM6MS0yMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzM6NTgtMjMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzM6NTgtMjMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzM6MzItMjMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM0OjEtMjM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjQ3LTIzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM0OjQ3LTIzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM0OjMyLTIzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzU6MS0yMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzU6NTgtMjM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzU6NTgtMjM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzU6MzItMjM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM2OjEtMjM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM2OjU5LTIzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM2OjU5LTIzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM2OjMyLTIzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzc6MS0yMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzc6NjAtMjM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzc6NjAtMjM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzc6MzItMjM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzg6MS0yMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzg6NTktMjM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzg6NTktMjM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzg6MzItMjM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOToxLTIzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOTo2MS0yMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzOTo2MS0yMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzOTozMi0yMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NjAtMjQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NjAtMjQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTMtMjQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTMtMjQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MjoxLTI0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MjozMi0yNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDM6MS0yNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDM6NTAtMjQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDM6NTAtMjQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDM6MzItMjQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NDoxLTI0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NDozMi0yNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NTgtMjQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NTgtMjQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ2OjEtMjQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ2OjU5LTI0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ2OjU5LTI0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ2OjMyLTI0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDc6MS0yNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDc6NTQtMjQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDc6NTQtMjQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDc6MzItMjQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDg6MS0yNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDg6NTQtMjQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDg6NTQtMjQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDg6MzItMjQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NDYtMjQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NDYtMjQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUwOjEtMjUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUwOjU1LTI1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUwOjU1LTI1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUwOjMyLTI1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjYxLTI1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjYxLTI1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MzoxLTI1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MzozMi0yNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NDoxLTI1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NDozMi0yNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NDctMjU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NDctMjU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NjoxLTI1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Njo1MC0yNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Njo1MC0yNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NjozMi0yNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjUxLTI1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjUxLTI1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU4OjEtMjU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU4OjUxLTI1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU4OjUxLTI1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU4OjMyLTI1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjU0LTI1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjU0LTI1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ2LTI2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ2LTI2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MToxLTI2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MTo1NC0yNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2MTo1NC0yNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTozMi0yNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MjoxLTI2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MjozMi0yNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjM6MS0yNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjM6NDUtMjYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjM6NDUtMjYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjM6MzItMjYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NTQtMjY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NTQtMjY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTUtMjY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTUtMjY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU0LTI2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU0LTI2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY3OjEtMjY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY3OjMyLTI2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjg6MS0yNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjg6NDQtMjY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjg6NDQtMjY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjg6MzItMjY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2OToxLTI2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2OTozMi0yNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MDoxLTI3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDo1NC0yNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MDo1NC0yNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MDozMi0yNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MToxLTI3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MTo1My0yNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MTo1My0yNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MTozMi0yNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcyOjEtMjcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjQzLTI3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcyOjQzLTI3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcyOjMyLTI3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MzoxLTI3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MzozMi0yNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NToxLTI3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NTo1My0yNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NTo1My0yNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NTozMi0yNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc2OjEtMjc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc2OjYzLTI3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc2OjYzLTI3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc2OjMyLTI3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc3OjEtMjc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc3OjUzLTI3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc3OjUzLTI3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc3OjMyLTI3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzg6MS0yNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzg6NjAtMjc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzg6NjAtMjc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzg6MzItMjc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzk6MS0yNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzk6NDgtMjc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzk6NDgtMjc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzItMjc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDgtMjgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDgtMjgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NDQtMjgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NDQtMjgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4MjoxLTI4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Mjo0My0yODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Mjo0My0yODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4MjozMi0yODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NDYtMjgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NDYtMjgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg0OjEtMjg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg0OjQzLTI4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg0OjQzLTI4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg0OjMyLTI4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NToxLTI4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4NTo0NS0yODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4NTo0NS0yODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NTozMi0yODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg3OjEtMjg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg3OjMyLTI4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1NC0yODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1NC0yODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo1Mi0yODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo1Mi0yODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTA6MS0yOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTA6NDYtMjkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTA6NDYtMjkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTA6MzItMjkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkxOjEtMjkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkxOjY1LTI5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkxOjY1LTI5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkxOjMyLTI5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTI6MS0yOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTI6NTktMjkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTI6NTktMjkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTI6MzItMjkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MzoxLTI5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MzozMi0yOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NDoxLTI5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NDozMi0yOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NjAtMjk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NjAtMjk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTY6MS0yOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTY6NTgtMjk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTY6NTgtMjk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTY6MzItMjk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk3OjEtMjk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk3OjYwLTI5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk3OjYwLTI5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjMyLTI5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk4OjEtMjk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk4OjUxLTI5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk4OjUxLTI5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk4OjMyLTI5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk5OjEtMjk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjYyLTI5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjYyLTI5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMyLTI5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDA6MS0zMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDA6NjUtMzAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDA6NjUtMzAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDA6MzItMzAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMTo0My0zMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMTo0My0zMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMTozMi0zMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDI6MS0zMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDItMzAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDItMzAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzItMzAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDM6NjEtMzAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDM6NjEtMzAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDM6MzItMzAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA0OjEtMzA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA0OjUxLTMwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA0OjUxLTMwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA0OjMyLTMwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA1OjUwLTMwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA1OjUwLTMwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjMyLTMwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDY6MS0zMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6NDMtMzA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6NDMtMzA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzItMzA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA3OjEtMzA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA3OjU2LTMwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA3OjU2LTMwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA3OjMyLTMwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDg6MS0zMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDg6NTUtMzA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDg6NTUtMzA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDg6MzItMzA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA5OjEtMzA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA5OjU0LTMwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA5OjU0LTMwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA5OjMyLTMwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEwOjEtMzEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEwOjU5LTMxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEwOjU5LTMxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEwOjMyLTMxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTE6MS0zMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTE6NTItMzExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTE6NTItMzExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTE6MzItMzExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEyOjEtMzEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEyOjQ4LTMxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEyOjQ4LTMxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEyOjMyLTMxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEzOjEtMzEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEzOjU2LTMxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEzOjU2LTMxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEzOjMyLTMxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTQ6MS0zMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTQ6NTAtMzE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTQ6NTAtMzE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTQ6MzItMzE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxNTozMi0zMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE2OjEtMzE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE2OjU0LTMxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE2OjU0LTMxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE2OjMyLTMxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE3OjEtMzE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE3OjUyLTMxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE3OjUyLTMxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE3OjMyLTMxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxODoxLTMxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxODo1NC0zMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxODo1NC0zMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxODozMi0zMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxOToxLTMxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxOTo1NC0zMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxOTo1NC0zMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxOTozMi0zMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyMDoxLTMyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyMDozMi0zMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzIxOjEtMzIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzIxOjUwLTMyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzIxOjUwLTMyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzIxOjMyLTMyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjI6MS0zMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjI6NTctMzIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjI6NTctMzIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjI6MzItMzIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjM6MS0zMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjM6NDctMzIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjM6NDctMzIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjM6MzItMzIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNDoxLTMyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNDozMi0zMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjU6MS0zMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjU6NTItMzI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjU6NTItMzI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjU6MzItMzI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI2OjEtMzI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI2OjQ2LTMyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI2OjQ2LTMyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI2OjMyLTMyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNzoxLTMyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNzo1My0zMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNzo1My0zMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNzozMi0zMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI4OjEtMzI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI4OjYwLTMyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI4OjYwLTMyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI4OjMyLTMyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI5OjEtMzI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI5OjU2LTMyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI5OjU2LTMyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI5OjMyLTMyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzA6MS0zMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzA6NjItMzMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzA6NjItMzMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzA6MzItMzMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMToxLTMzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMTozMi0zMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzI6MS0zMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzI6NTYtMzMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzI6NTYtMzMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzI6MzItMzMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMzoxLTMzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMzozMi0zMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNDoxLTMzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNDozMi0zMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNToxLTMzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNTozMi0zMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzY6MS0zMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzY6NDktMzM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzY6NDktMzM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzY6MzItMzM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM3OjEtMzM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM3OjQ4LTMzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM3OjQ4LTMzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM3OjMyLTMzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM4OjEtMzM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM4OjUwLTMzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM4OjUwLTMzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM4OjMyLTMzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzk6MS0zMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzk6NDgtMzM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzk6NDgtMzM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzk6MzItMzM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDA6MS0zNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDA6NTQtMzQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDA6NTQtMzQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDA6MzItMzQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDE6MS0zNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDE6NTQtMzQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDE6NTQtMzQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDE6MzItMzQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDI6MS0zNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDI6NTYtMzQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDI6NTYtMzQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDI6MzItMzQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0MzoxLTM0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0MzozMi0zNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ0OjEtMzQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ0OjU2LTM0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ0OjU2LTM0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ0OjMyLTM0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDU6MS0zNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDU6NDctMzQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDU6NDctMzQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDU6MzItMzQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0NjoxLTM0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0NjozMi0zNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ3OjEtMzQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ3OjU1LTM0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ3OjU1LTM0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ3OjMyLTM0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0ODoxLTM0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0ODozMi0zNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDk6MS0zNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDk6NTEtMzQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDk6NTEtMzQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDk6MzItMzQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTA6MS0zNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTA6NTAtMzUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTA6NTAtMzUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTA6MzItMzUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1MToxLTM1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1MTo1My0zNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1MTo1My0zNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1MTozMi0zNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzUyOjEtMzUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzUyOjUwLTM1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzUyOjUwLTM1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzUyOjMyLTM1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTM6MS0zNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTM6NDYtMzUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTM6NDYtMzUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTM6MzItMzUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU0OjEtMzU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU0OjQ3LTM1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU0OjQ3LTM1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU0OjMyLTM1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTU6MS0zNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTU6NTUtMzU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTU6NTUtMzU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTU6MzItMzU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU2OjEtMzU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU2OjU3LTM1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU2OjU3LTM1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU2OjMyLTM1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU3OjEtMzU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU3OjU1LTM1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU3OjU1LTM1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU3OjMyLTM1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1ODoxLTM1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1ODozMi0zNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU5OjEtMzU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU5OjQ1LTM1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU5OjQ1LTM1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU5OjMyLTM1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYwOjEtMzYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYwOjQ4LTM2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYwOjQ4LTM2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYwOjMyLTM2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYxOjEtMzYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYxOjYyLTM2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYxOjYyLTM2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYxOjMyLTM2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjI6MS0zNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjI6NDktMzYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjI6NDktMzYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjI6MzItMzYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYzOjEtMzYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYzOjQxLTM2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYzOjQxLTM2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYzOjMyLTM2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjQ6MS0zNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjQ6NDUtMzY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjQ6NDUtMzY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjQ6MzItMzY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjU6MS0zNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjU6NTgtMzY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjU6NTgtMzY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjU6MzItMzY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY2OjEtMzY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY2OjU4LTM2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY2OjU4LTM2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY2OjMyLTM2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2NzoxLTM2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2NzozMi0zNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2ODoxLTM2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2ODozMi0zNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY5OjEtMzY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY5OjU2LTM2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY5OjU2LTM2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY5OjMyLTM2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNzA6MS0zNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNzA6NjEtMzcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzA6NjEtMzcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNzA6MzItMzcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzcxOjEtMzcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzcxOjYxLTM3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzcxOjYxLTM3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzcxOjMyLTM3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3MjoxLTM3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3MjozMi0zNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzczOjEtMzczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzczOjU4LTM3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzczOjU4LTM3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzczOjMyLTM3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NDoxLTM3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3NDo1My0zNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3NDo1My0zNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NDozMi0zNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc1OjEtMzc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc1OjU1LTM3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc1OjU1LTM3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc1OjMyLTM3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NjoxLTM3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Njo1NC0zNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Njo1NC0zNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NjozMi0zNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NzoxLTM3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NzozMi0zNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc4OjEtMzc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc4OjQ1LTM3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc4OjQ1LTM3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc4OjMyLTM3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc5OjEtMzc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc5OjQ3LTM3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc5OjQ3LTM3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc5OjMyLTM3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODA6MS0zODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODA6NDUtMzgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODA6NDUtMzgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODA6MzItMzgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODE6MS0zODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODE6NTctMzgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODE6NTctMzgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODE6MzItMzgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODI6MS0zODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODI6NjAtMzgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODI6NjAtMzgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODI6MzItMzgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODM6MS0zODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODM6NTEtMzgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODM6NTEtMzgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODM6MzItMzgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODQ6MS0zODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODQ6NTEtMzg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODQ6NTEtMzg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODQ6MzItMzg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODU6MS0zODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODU6NjItMzg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODU6NjItMzg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODU6MzItMzg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4NjoxLTM4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4NjozMi0zODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODc6MS0zODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODc6NTMtMzg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODc6NTMtMzg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODc6MzItMzg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4ODoxLTM4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4ODozMi0zODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4OToxLTM4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4OTozMi0zODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkwOjEtMzkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkwOjQ2LTM5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkwOjQ2LTM5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkwOjMyLTM5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5MToxLTM5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5MTozOC0zOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5MTozOC0zOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5MTozMi0zOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkyOjEtMzkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkyOjMyLTM5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTM6MS0zOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTM6NDUtMzkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTM6NDUtMzkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTM6MzItMzkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTQ6MS0zOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTQ6NDUtMzk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTQ6NDUtMzk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTQ6MzItMzk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTU6MS0zOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTU6NDctMzk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTU6NDctMzk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTU6MzItMzk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5NjoxLTM5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5NjozMi0zOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTc6MS0zOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTc6NDctMzk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTc6NDctMzk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTc6MzItMzk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5ODoxLTM5ODo1MyIgbmFtZT0iVG9Cb29sZWFuIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTg6NDMtMzk4OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTg6NDMtMzk4OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTg6MzMtMzk4OjM5IiBuYW1lPSJmaGlyOmJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTk6MS0zOTk6NDciIG5hbWU9IlRvRGF0ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzk5OjM3LTM5OTo0NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzk5OjM3LTM5OTo0MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzk5OjMwLTM5OTozMyIgbmFtZT0iZmhpcjpkYXRlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAwOjEtNDAwOjU1IiBuYW1lPSJUb0RhdGVUaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDA6NDUtNDAwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MDA6NDUtNDAwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0MDA6MzQtNDAwOjQxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAxOjEtNDAxOjUzIiBuYW1lPSJUb0RlY2ltYWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMTo0My00MDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMTo0My00MDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMTozMy00MDE6MzkiIG5hbWU9ImZoaXI6ZGVjaW1hbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwMjoxLTQwMjo1NCIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDAyOjQ0LTQwMjo1NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDAyOjQ0LTQwMjo0OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDAyOjM0LTQwMjo0MCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAzOjEtNDAzOjUzIiBuYW1lPSJUb0ludGVnZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMzo0My00MDM6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMzo0My00MDM6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMzozMy00MDM6MzkiIG5hbWU9ImZoaXI6aW50ZWdlciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNDoxLTQwNDo1MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNDo0MS00MDQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNDo0MS00MDQ6NDUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNDozMi00MDQ6MzciIG5hbWU9ImZoaXI6c3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA1OjEtNDA1OjQ3IiBuYW1lPSJUb1RpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNTozNy00MDU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNTozNy00MDU6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNTozMC00MDU6MzMiIG5hbWU9ImZoaXI6dGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNjoxLTQwNjo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNjozOC00MDY6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNjozOC00MDY6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNjozMi00MDY6MzQiIG5hbWU9ImZoaXI6dXJpIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA3OjEtNDA3OjUwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDA3OjQwLTQwNzo1MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA3OjQwLTQwNzo0NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDA3OjMyLTQwNzozNiIgbmFtZT0iZmhpcjp4aHRtbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgbGlicmFyeSBkZWZpbmVzIGZ1bmN0aW9ucyB0byBjb252ZXJ0IGJldHdlZW4gRkhJUlxuIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydFxuIEZISVJQYXRoIGltcGxlbWVudGF0aW9uLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIHRoZSBGSElSSGVscGVycyB3aWtpIHBhZ2U6XG4gaHR0cHM6Ly9naXRodWIuY29tL2NxZnJhbWV3b3JrL2NsaW5pY2FsX3F1YWxpdHlfbGFuZ3VhZ2Uvd2lraS9GSElSSGVscGVycyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiYWxsb3dGbHVlbnQiLAogICAgICAgICAgICAidmFsdWUiIDogInRydWUiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiwKICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTExOjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAic3RhdGVtZW50cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzoxLTIwOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6NS0yMDo2MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6OC0xNDoyMSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDo4LTE0OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6OS0xNToxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo5LTIwOjYyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6MTItMTc6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxMy0xODo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MjItMTg6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjIyLTE4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoyMi0xODoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo0NC0xODo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJlbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxMy0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoyMi0yMDo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MjItMjA6MzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjIyLTIwOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjQ0LTIwOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzozNS0xMzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTMzOjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NS0zMzo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY29tcGFyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEwLTIzOjEzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6OS0yNDozNiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjE0LTI0OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjI0LTI0OjM2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbGxpc2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6OS0yNTozMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjE0LTI1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6MjMtMjU6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAic2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6OS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjE0LTI2OjE4IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyNS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtaW51dGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzo5LTI3OjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6MTQtMjc6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyMy0yNzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJob3VyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6OS0yODoyNyIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OjE0LTI4OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MjMtMjg6MjciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZGF5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6OS0yOToyOSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjE0LTI5OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndrIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjI0LTI5OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndlZWsiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo5LTMwOjMwIiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTQtMzA6MTciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MjQtMzA6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW9udGgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTo5LTMxOjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTQtMzE6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiYSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToyMy0zMToyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxNC0zMjoxNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjM3LTIyOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS00Njo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6NS00Njo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzo5LTM3OjM5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6OS0zODo0NSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxNC0zODoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDItMzg6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjktNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTMtNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjIxLTQwOjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MjctNDA6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MzMtNDA6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5Db21wYXJhdG9yUXVhbnRpdHlOb3RTdXBwb3J0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjkwLTQwOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjk5LTQwOjE4OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSIFF1YW50aXR5IHZhbHVlIGhhcyBhIGNvbXBhcmF0b3IgYW5kIGNhbm5vdCBiZSBjb252ZXJ0ZWQgdG8gYSBTeXN0ZW0uUXVhbnRpdHkgdmFsdWUuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjktNDM6MTM4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MTQtNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MTozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo0MS00MTo5MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjQxLTQxOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjY1LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxOC00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjE4LTQyOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjQyLTQyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMy00MzoxMzgiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzozOC00Mzo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MzgtNDM6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjM4LTQzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6NjYtNDM6MTM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ2FsZW5kYXJVbml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo4MS00MzoxMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6OTAtNDM6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjExMS00MzoxMTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjEzMi00MzoxMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxMy00NToyMDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyNy00NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Qm9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAidHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29kZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozMy00NTo3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojc5LTQ1Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTIxLTQ1OjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTQzLTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTUwLTQ1OjE1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTc0LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ8IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTgwLTQ1OjE4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjAyLTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzctMzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTU3OjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0OTo1LTU3OjciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICJjYXNlSXRlbSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjktNTA6MzkiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyOSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjM2LTUwOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo5LTUxOjQ1IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MzUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjE0LTUxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0Mi01MTo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6OS01NDoxMzgiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxNC01Mzo5MiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjQxLTUyOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDEtNTI6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NjUtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjE4LTUzOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTgtNTM6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NDItNTM6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEzLTU0OjEzOCIsCiAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjM4LTU0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDozOC01NDo1MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MzgtNTQ6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo2Ni01NDoxMzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjgxLTU0OjEzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo5MC01NDoxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTExLTU0OjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTMyLTU0OjEzNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEzLTU2OjIwNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMS01NjoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjI3LTU2OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMzLTU2Ojc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NzktNTY6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkludmFsaWQgRkhJUiBRdWFudGl0eSBjb2RlOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxMjEtNTY6MTI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNDMtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiAoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNTAtNTY6MTU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNzQtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxODAtNTY6MTg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMDItNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo1NS00ODo2NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtODQ6MTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1LTg0OjExIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo4LTYwOjIzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjgtNjA6MTUiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMwLTYwOjMzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjktODQ6MTEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICAgICJjb21wYXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MTQtNjE6MzgiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MjoxMy02NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjE4LTYyOjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjwiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MTctNjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjIxLTY0OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MjEtNjU6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzoxMy03MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjE4LTY3OjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjw9IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjE3LTcxOjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjIxLTY5OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MjEtNzA6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTAtNzA6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxMy03NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjE4LTcyOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj49IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjE3LTc2OjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjIxLTc0OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUwLTc0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToyMS03NToyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzoxMy04MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjE4LTc3OjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj4iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTctODE6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjIxLTc5OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjUwLTc5OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDoyMS04MDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjE3LTgzOjY4IiwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI2LTgzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjM3LTgzOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo0OC04Mzo2NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1OS04Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozNy01OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjEtOTA6MTA3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUmF0aW8iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NS05MDoxMDciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjktODg6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo5LTkwOjEwNyIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIm51bWVyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDozNS05MDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo0Ni05MDo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjQ2LTkwOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NzctOTA6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjg4LTkwOjEwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkZW5vbWluYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6ODgtOTA6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMxLTg2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MS05Njo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjUtOTY6NjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjgtOTM6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6OC05MzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6OS05NDoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo5LTk2OjYzIiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjE4LTk2OjM4IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjI5LTk2OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MjktOTY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NDEtOTY6NjIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MzQtOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoxLTEwNzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1LTEwNzo5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo4LTk5OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjgtOTk6MTMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6OS0xMDA6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6OS0xMDc6OSIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzoxNy0xMDM6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjE5LTEwNDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToyMC0xMDU6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MjAtMTA2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMS05ODo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MS0xMTY6OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjUtMTE2OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDo4LTExMDoyMiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6OC0xMTA6MTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjktMTExOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjktMTE2OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGVzIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjIwLTExNDozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozNy0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6NDQtMTE0OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidGV4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjIyLTExNToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MzUtMTA5OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTEyMjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjUtMTIyOjYwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6OC0xMTk6MjQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjgtMTE5OjE2IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIwOjktMTIwOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjktMTIyOjYwIiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjQ4LTEyMjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzctMTE4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI0OjEtMTI0OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDozNS0xMjQ6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MS0xMjU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAicmVzb2x2ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI1OjM1LTEyNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjY6MS0xMjY6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozNi0xMjY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6ODYiLAogICAgICAgICAgICAibmFtZSIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjM1LTEyNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjQ4LTEyNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo4OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI4OjM2LTEyODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODo1MC0xMjg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MS0xMjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiaGFzVmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MzQtMTI5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MS0xMzA6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiZ2V0VmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzQtMTMwOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MS0xMzE6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAib2ZUeXBlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkZW50aWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMxOjM1LTEzMTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjoxLTEzMjo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjozMS0xMzI6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MS0xMzM6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiYXMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzEtMTMzOjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0Ojg2IiwKICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnREZWZpbml0aW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjQzLTEzNDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1Ojk1IiwKICAgICAgICAgICAgIm5hbWUiIDogInNsaWNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjMxLTEzNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ0LTEzNTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibmFtZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzU6NTctMTM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjEtMTM2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImNoZWNrTW9kaWZpZXJzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo0MS0xMzY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6OTMiLAogICAgICAgICAgICAibmFtZSIgOiAiY2hlY2tNb2RpZmllcnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjQxLTEzNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODoxLTEzODo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo0MC0xMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxLTEzOTo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo0MC0xMzk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjU4LTEzOTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxLTE0MDo4OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjb25mb3Jtc1RvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNy0xNDA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN0cnVjdHVyZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6NTctMTQwOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjc4IiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjMxLTE0MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1jb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6NDYtMTQxOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MzMtMTQyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWVTZXQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjUwLTE0Mjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoxLTE0Mzo5MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJtZW1iZXJPZiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzozNC0xNDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZWFibGVDb25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6NjAtMTQzOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEtMTQ0Ojg4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MzMtMTQ0OjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtZWRDb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjU2LTE0NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MS0xNDU6MTA4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjM0LTE0NTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVkQ29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NjctMTQ1OjgxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxLTE0Njo5MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MzUtMTQ2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtaW5nQ29kaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjExMSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjM2LTE0Nzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWluZ0NvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjcwLTE0Nzo4NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MS0xNDg6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiaHRtbENoZWNrcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODozNi0xNDg6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjY291bnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MS0xNTE6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjMyLTE1MTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzoxLTE1Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MzItMTUzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTQ6MS0xNTQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjMyLTE1NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjEtMTU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NjozMi0xNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NzoxLTE1Nzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MzItMTU3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlcXVpcmVkQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTg6MS0xNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjMyLTE1ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5RGVmaW5pdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjEtMTYyOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MjozMi0xNjI6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRkcmVzc1VzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkbWluaXN0cmF0aXZlR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjEtMTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDozMi0xNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdncmVnYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjEtMTY2OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NjozMi0xNjY6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjc6MS0xNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjMyLTE2Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODoxLTE2ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6MzItMTY4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjEtMTcxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTozMi0xNzE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MjoxLTE3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MzItMTcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbk9wZXJhdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzY6MS0xNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjMyLTE3Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpbmRpbmdTdHJlbmd0aCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4OjczIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDoxLTE4MDo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjgzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MzItMTgwOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgxOjEtMTgxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTozMi0xODE6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QnVuZGxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5BY3Rpdml0eUtpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg1OjEtMTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTozMi0xODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NzoxLTE4Nzo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MzItMTg3OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVUZWFtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg4OjEtMTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODozMi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjEtMTg5OjgzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTozMi0xODk6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MS0xOTA6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjMyLTE5MDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DaGFyZ2VJdGVtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjoxLTE5Mjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6MzItMTkyOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNsYWltU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk0OjEtMTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDozMi0xOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTU6MS0xOTU6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjMyLTE5NTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtQ29udGVudE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MS0xOTY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjMyLTE5Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzoxLTE5Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6MzItMTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjEtMTk5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozMi0xOTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tbXVuaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMDoxLTIwMDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MzItMjAwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMToxLTIwMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MzItMjAxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjEtMjAzOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzozMi0yMDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MS0yMDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjMyLTIwNDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwRXF1aXZhbGVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb25hbERlbGV0ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsUmVhZFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnREYXRhTWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOToxLTIwOTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6MzItMjA5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnRQcm92aXNpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEwOjEtMjEwOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDozMi0yMTA6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjExOjEtMjExOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTozMi0yMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc3RyYWludFNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50U3lzdGVtIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEzOjEtMjEzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzozMi0yMTM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE0OjEtMjE0OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDozMi0yMTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnRyYWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjEtMjE2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjozMi0yMTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJpYnV0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjEtMjE3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzozMi0yMTc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q292ZXJhZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MS0yMTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjMyLTIxODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DdXJyZW5jeUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjA6MS0yMjA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjMyLTIyMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlzT2ZXZWVrIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MS0yMjQ6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjMyLTIyNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6MS0yMjU6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjMyLTIyNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNjoxLTIyNjo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MzItMjI2OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NvbG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI3OjEtMjI3Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzozMi0yMjc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MS0yMjg6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjMyLTIyODo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VOYW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOToxLTIyOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MzItMjI5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaWFnbm9zdGljUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMyOjEtMjMyOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjozMi0yMzI6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGlzY3JpbWluYXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MS0yMzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjMyLTIzMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudENvbmZpZGVudGlhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDoxLTIzNDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MzItMjM0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNToxLTIzNTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MzItMjM1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjEtMjM2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjozMi0yMzY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM3OjEtMjM3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzozMi0yMzc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzODoxLTIzODo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MzItMjM4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOToxLTIzOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MzItMjM5OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuYWJsZVdoZW5CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MjoxLTI0Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MzItMjQyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuY291bnRlckxvY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjEtMjQzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MzozMi0yNDM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjEtMjQ0OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDozMi0yNDQ6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5kcG9pbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbnJvbGxtZW50UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NjoxLTI0Njo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MzItMjQ2OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NzoxLTI0Nzo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MzItMjQ3OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVwaXNvZGVPZkNhcmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MS0yNDg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjMyLTI0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXZlbnRUaW1pbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MS0yNTA6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjMyLTI1MDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmlkZW5jZVZhcmlhYmxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjEtMjUzOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzozMi0yNTM6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwb3N1cmVTdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxLTI1NDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6MzItMjU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4dGVuc2lvbkNvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkFsbFR5cGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjEtMjU2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NjozMi0yNTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRlZmluZWRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRldmljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1ODoxLTI1ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MzItMjU4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJSZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSU3Vic3RhbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlZlcnNpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MS0yNjE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjMyLTI2MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GYW1pbHlIaXN0b3J5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjEtMjYyOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozMi0yNjI6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RmlsdGVyT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjM6MS0yNjM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjMyLTI2Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GbGFnU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R29hbExpZmVjeWNsZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyYXBoQ29tcGFydG1lbnRSdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NzoxLTI2Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MzItMjY3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwTWVhc3VyZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODoxLTI2ODo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6MzItMjY4OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI2OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6MzItMjY5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRhbmNlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6MS0yNzA6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjMyLTI3MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhZ2VHZW5lcmF0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjEtMjcxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTozMi0yNzE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGVQYXJhbWV0ZXJDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcyOjEtMjcyOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozMi0yNzI6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SFRUUFZlcmIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MS0yNzM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjMyLTI3Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGlmaWVyVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NToxLTI3NTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6MzItMjc1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltYWdpbmdTdHVkeVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NjoxLTI3Njo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MzItMjc2OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzc6MS0yNzc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjMyLTI3Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbW11bml6YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzg6MS0yNzg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjMyLTI3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjEtMjc5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozMi0yNzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW52b2ljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Jc3N1ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODI6MS0yODI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjMyLTI4MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaW5rVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmthZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjEtMjg0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDozMi0yODQ6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODU6MS0yODU6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjMyLTI4NTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjEtMjg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzozMi0yODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVhc3VyZVJlcG9ydFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTA6MS0yOTA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjMyLTI5MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpYVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MToxLTI5MTo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjc1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MzItMjkxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MjoxLTI5Mjo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MzItMjkyOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MzoxLTI5Mzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MzItMjkzOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTQ6MS0yOTQ6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjMyLTI5NDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdEludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MS0yOTY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjMyLTI5Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzoxLTI5Nzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6MzItMjk3OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MS0yOTg6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMyLTI5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk5OjEtMjk5OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMi0yOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjEtMzAwOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDozMi0zMDA6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTozMi0zMDE6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWltZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDI6MS0zMDI6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo1MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMyLTMwMjozOCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1lVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzozMi0zMDM6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MS0zMDQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjMyLTMwNDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjEtMzA1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTozMi0zMDU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFycmF0aXZlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA2OjEtMzA2OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMi0zMDY6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Tm90ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MS0zMDc6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjMyLTMwNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpaW9uT3JkZXJJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MS0zMDg6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMyLTMwODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpb25PcmRlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOToxLTMwOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6MzItMzA5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uRGF0YVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTA6MS0zMTA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjMyLTMxMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTE6MS0zMTE6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjMyLTMxMTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxMjoxLTMxMjo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6MzItMzEyOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTM6MS0zMTM6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjMyLTMxMzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcGVyYXRpb25QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTQ6MS0zMTQ6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjMyLTMxNDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcmllbnRhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjMyLTMxNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6MS0zMTY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjMyLTMxNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFJlcXVpcmVkIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE3OjEtMzE3OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzozMi0zMTc6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGFydGljaXBhbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTg6MS0zMTg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjMyLTMxODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE5OjEtMzE5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTozMi0zMTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudE5vdGljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMDoxLTMyMDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6MzItMzIwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMToxLTMyMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6MzItMzIxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMjoxLTMyMjo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6MzItMzIyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5UmVwcmVzZW50YXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjM6MS0zMjM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjMyLTMyMzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9wZXJ0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjQ6MS0zMjQ6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjMyLTMyNDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm92ZW5hbmNlRW50aXR5Um9sZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNToxLTMyNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6MzItMzI1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI2OjEtMzI2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjozMi0zMjY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbGl0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjc6MS0zMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjMyLTMyNzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eUNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjg6MS0zMjg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjMyLTMyODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI5OjEtMzI5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTozMi0zMjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZUl0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMwOjEtMzMwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDozMi0zMzA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMxOjEtMzMxOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTozMi0zMzE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzI6MS0zMzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjMyLTMzMjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VWZXJzaW9uUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzM6MS0zMzM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjMyLTMzMzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcnJlZERvY3VtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM0OjEtMzM0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDozMi0zMzQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVsYXRlZEFydGlmYWN0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNToxLTMzNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6MzItMzM1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlbWl0dGFuY2VPdXRjb21lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM2OjEtMzM2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjozMi0zMzY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVwb3NpdG9yeVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzc6MS0zMzc6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjMyLTMzNzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM4OjEtMzM4OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODozMi0zMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM5OjEtMzM5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTozMi0zMzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MDoxLTM0MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6MzItMzQwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoRWxlbWVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDE6MS0zNDE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjMyLTM0MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQyOjEtMzQyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MjozMi0zNDI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hTdWJqZWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQzOjEtMzQzOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MzozMi0zNDM6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ0OjEtMzQ0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDozMi0zNDQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VWZXJzaW9uUG9saWN5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ1OjEtMzQ1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTozMi0zNDU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzcG9uc2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ2OjEtMzQ2OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NjozMi0zNDY6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ3OjEtMzQ3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NzozMi0zNDc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Umlza0Fzc2Vzc21lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDg6MS0zNDg6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjMyLTM0ODo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TUERYTGljZW5zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OToxLTM0OTo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6MzItMzQ5OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaENvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTA6MS0zNTA6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjMyLTM1MDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hFbnRyeU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTE6MS0zNTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjMyLTM1MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hNb2RpZmllckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTI6MS0zNTI6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjMyLTM1Mjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hQYXJhbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTM6MS0zNTM6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjMyLTM1Mzo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWN0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NDoxLTM1NDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6MzItMzU0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcXVlbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NToxLTM1NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6MzItMzU1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU2OjEtMzU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NjozMi0zNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NzoxLTM1Nzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6MzItMzU3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU4OjEtMzU4OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODozMi0zNTg6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xpY2luZ1J1bGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU5OjEtMzU5OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTozMi0zNTk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xvdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MDoxLTM2MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6MzItMzYwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNvcnREaXJlY3Rpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjE6MS0zNjE6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjMyLTM2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjI6MS0zNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjMyLTM2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MzoxLTM2Mzo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6MzItMzYzOjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NDoxLTM2NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6MzItMzY0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cmFuZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjU6MS0zNjU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjMyLTM2NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NjoxLTM2Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6MzItMzY2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcENvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY3OjEtMzY3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NzozMi0zNjc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2ODoxLTM2ODo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6MzItMzY4OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcElucHV0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OToxLTM2OTo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6MzItMzY5OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcE1vZGVsTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MDoxLTM3MDo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6MzItMzcwOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzcxOjEtMzcxOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTozMi0zNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzI6MS0zNzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjMyLTM3Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzM6MS0zNzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjMyLTM3Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdWJzY3JpcHRpb25DaGFubmVsVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NDoxLTM3NDo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6MzItMzc0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NToxLTM3NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6MzItMzc1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1cHBseURlbGl2ZXJ5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzc2OjEtMzc2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NjozMi0zNzY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NzoxLTM3Nzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6MzItMzc3OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3ODoxLTM3ODo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6MzItMzc4OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzk6MS0zNzk6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjMyLTM3OTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrUHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODA6MS0zODA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjMyLTM4MDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzgxOjEtMzgxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTozMi0zODE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydEFjdGlvblJlc3VsdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MjoxLTM4Mjo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6MzItMzgyOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODM6MS0zODM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjMyLTM4Mzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0UmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg0OjEtMzg0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDozMi0zODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NToxLTM4NTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6MzItMzg1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NjoxLTM4Njo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6MzItMzg2OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRyaWdnZXJUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg3OjEtMzg3OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NzozMi0zODc6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZURlcml2YXRpb25SdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg4OjEtMzg4OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODozMi0zODg6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OToxLTM4OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6MzItMzg5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVESUVudHJ5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MDoxLTM5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6MzItMzkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVuaXRzT2ZUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkxOjEtMzkxOjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozMi0zOTE6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkyOjEtMzkyOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MjozMi0zOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkzOjEtMzkzOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MzozMi0zOTM6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uQmFzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NDoxLTM5NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6MzItMzk0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvbkV5ZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTU6MS0zOTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjMyLTM5NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTY6MS0zOTY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjMyLTM5Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1YUGF0aFVzYWdlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NzoxLTM5Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6MzItMzk3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJhc2U2NEJpbmFyeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5ODoxLTM5ODo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0Jvb2xlYW4iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjMzLTM5ODozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1ib29sZWFuIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk5OjEtMzk5OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQ3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzAtMzk5OjMzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MS00MDA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MzQtNDAwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDAxOjEtNDAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGVjaW1hbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6MzMtNDAxOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRlY2ltYWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MS00MDI6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MzQtNDAyOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWluc3RhbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDM6MS00MDM6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlZ2VyIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzozMy00MDM6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNDoxLTQwNDo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6MzItNDA0OjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNToxLTQwNTo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1RpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjMwLTQwNTozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn10aW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA2OjEtNDA2OjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozMi00MDY6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dXJpIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA3OjEtNDA3OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NTAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzozMi00MDc6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9eGh0bWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/FHIRHelpers" - } - }, - { - "resource": { - "resourceType": "Patient", - "id": "CMSTest-patient-2", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2028-9", - "display": "Asian" - } - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2135-2", - "display": "Hispanic or Latino" - } - } - ] - } - ], - "identifier": [ - { - "use": "usual", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MR", - "display": "Medical Record Number" - } - ] - }, - "system": "http://hospital.smarthealthit.org", - "value": "999999999" - } - ], - "name": [ - { - "family": "Dere", - "given": [ - "Ben" - ] - } - ], - "gender": "female", - "birthDate": "2000-03-01" - }, - "request": { - "method": "PUT", - "url": "Patient/CMSTest-patient-2" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "SDE", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/SDE", - "name": "SDE", - "title": "Library - Supplemental Data Elements", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "A Shared library encapsulating valuable common functions related to the use of Supplemental Data Elements used in Alphora dQM and QICore-based CQL artifacts.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Value set Ethnicity", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837" - }, - { - "type": "depends-on", - "display": "Value set ONC Administrative Sex", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1" - }, - { - "type": "depends-on", - "display": "Value set Payer", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" - }, - { - "type": "depends-on", - "display": "Value set Race", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - }, - { - "name": "SDE Ethnicity", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Payer", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Race", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Coverage", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Coverage" - ], - "mustSupport": [ - "type", - "period", - "type.coding" - ], - "codeFilter": [ - { - "path": "type", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" - } - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBTREUKCi8qQHVwZGF0ZTogQEBCVFIgMjAyMC0wMy0zMSAtPgpJbmNyZW1lbnRlZCB2ZXJzaW9uIHRvIDIuMC4wClVwZGF0ZWQgRkhJUiB2ZXJzaW9uIHRvIDQuMC4xCkBAQCovCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCnZhbHVlc2V0ICJFdGhuaWNpdHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3Jwp2YWx1ZXNldCAiT05DIEFkbWluaXN0cmF0aXZlIFNleCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEnCnZhbHVlc2V0ICJQYXllciI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS4zNTkxJwp2YWx1ZXNldCAiUmFjZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMC0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIxLTAxLTAxVDAwOjAwOjAwLjAwMCkKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eScKICAgICAgICAgIHJldHVybiBFeHRlbnNpb24uZXh0ZW5zaW9uCiAgICApKSBFCiAgICAgIHdoZXJlIEUudXJsID0gJ29tYkNhdGVnb3J5JwogICAgICAgIG9yIEUudXJsID0gJ2RldGFpbGVkJwogICAgICByZXR1cm4gRkhJUkhlbHBlcnMuVG9Db2RlKEUudmFsdWUpCgpkZWZpbmUgIlNERSBQYXllciI6CiAgZmxhdHRlbihbQ292ZXJhZ2U6IHR5cGUgaW4gIlBheWVyIl0gUGF5ZXIKICAgIHdoZXJlIFBheWVyLnBlcmlvZCBvdmVybGFwcyAiTWVhc3VyZW1lbnQgUGVyaW9kIgogICAgICByZXR1cm4gKAogICAgICAgIFBheWVyLnR5cGUuY29kaW5nIGMKICAgICAgICAgIHJldHVybiBGSElSSGVscGVycy5Ub0NvZGUoYykKICAgICAgKQogICkKCmRlZmluZSAiU0RFIFJhY2UiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UnCiAgICAgICAgICByZXR1cm4gRXh0ZW5zaW9uLmV4dGVuc2lvbgogICAgKSkgRQogICAgICB3aGVyZSBFLnVybCA9ICdvbWJDYXRlZ29yeScKICAgICAgICBvciBFLnVybCA9ICdkZXRhaWxlZCcKICAgICAgcmV0dXJuIEZISVJIZWxwZXJzLlRvQ29kZShFLnZhbHVlKQoKZGVmaW5lICJTREUgU2V4IjoKICBjYXNlCiAgICAgIHdoZW4gUGF0aWVudC5nZW5kZXIgPSAnbWFsZScgdGhlbiBDb2RlIHsgY29kZTogJ00nLCBzeXN0ZW06ICdodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyJywgZGlzcGxheTogJ01hbGUnIH0KICAgICAgd2hlbiBQYXRpZW50LmdlbmRlciA9ICdmZW1hbGUnIHRoZW4gQ29kZSB7IGNvZGU6ICdGJywgc3lzdGVtOiAnaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlcicsIGRpc3BsYXk6ICdGZW1hbGUnIH0KICAgICAgZWxzZSBudWxsCiAgICBlbmQK" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iU0RFIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iODoxLTg6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9InVwZGF0ZSIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgIDwvZGVmPgogICA8L3VzaW5ncz4KICAgPGluY2x1ZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOjEtMTA6NDEiIGxvY2FsSWRlbnRpZmllcj0iRkhJUkhlbHBlcnMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIi8+CiAgIDwvaW5jbHVkZXM+CiAgIDxwYXJhbWV0ZXJzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3OjEtMTc6OTkiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxkZWZhdWx0IGxvY2F0b3I9IjE3OjQwLTE3Ojk5IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE3OjQ5LTE3OjcyIiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE3Ojc1LTE3Ojk4IiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2RlZmF1bHQ+CiAgICAgIDwvZGVmPgogICA8L3BhcmFtZXRlcnM+CiAgIDx2YWx1ZVNldHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjo4NyIgbmFtZT0iRXRobmljaXR5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo5NyIgbmFtZT0iT05DIEFkbWluaXN0cmF0aXZlIFNleCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0OjEtMTQ6ODQiIG5hbWU9IlBheWVyIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1OjEtMTU6ODIiIG5hbWU9IlJhY2UiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OjEtMTg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjEtMTg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDoxLTI4OjQwIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yODo0MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTozLTI1OjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yNTo2IiB4c2k6dHlwZT0iRmxhdHRlbiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjEyLTI1OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI6Ny0yMjozMyIgYWxpYXM9IkV4dGVuc2lvbiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyOjctMjI6MjMiIHBhdGg9ImV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjM6OS0yMzo5NyIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MTUtMjM6MjciIHBhdGg9InVybCIgc2NvcGU9IkV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MzEtMjM6OTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNDoxMS0yNDozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0OjE4LTI0OjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjY6Ny0yNzoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6MTMtMjY6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjEzLTI2OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyMS0yNjozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjEyLTI3OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoxMi0yNzoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjc6MjAtMjc6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI4OjctMjg6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODoxNC0yODo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OjMzLTI4OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zNzozIiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTozLTM3OjMiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzE6MTAtMzc6MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMToxMS0zMTo0MyIgYWxpYXM9IlBheWVyIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE6MTEtMzE6MzciIGRhdGFUeXBlPSJmaGlyOkNvdmVyYWdlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ292ZXJhZ2UiIGNvZGVQcm9wZXJ0eT0idHlwZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzE6MzAtMzE6MzYiIG5hbWU9IlBheWVyIiBwcmVzZXJ2ZT0idHJ1ZSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMjo1LTMyOjUyIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMyOjExLTMyOjIyIiBwYXRoPSJwZXJpb2QiIHNjb3BlPSJQYXllciIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzI6MzMtMzI6NTIiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMzOjctMzY6NyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzOjE0LTM2OjciIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ6OS0zNDoyNyIgYWxpYXM9ImMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDo5LTM0OjI1IiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0idHlwZSIgc2NvcGU9IlBheWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIzNToxMS0zNTozOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1OjE4LTM1OjM4IiBuYW1lPSJUb0NvZGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzU6MzciIG5hbWU9ImMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzk6MS00Nzo0MCIgbmFtZT0iU0RFIFJhY2UiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ3OjQwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwOjMtNDQ6OCIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ0OjYiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTItNDQ6NSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MTo3LTQxOjMzIiBhbGlhcz0iRXh0ZW5zaW9uIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDE6Ny00MToyMyIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSI0Mjo5LTQyOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxNS00MjoyNyIgcGF0aD0idXJsIiBzY29wZT0iRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjozMS00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvdXMvY29yZS9TdHJ1Y3R1cmVEZWZpbml0aW9uL3VzLWNvcmUtcmFjZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSI0MzoxMS00MzozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQzOjE4LTQzOjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iNDU6Ny00NjoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTMtNDU6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToyMS00NTozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ2OjEyLTQ2OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NjoxMi00NjoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDY6MjAtNDY6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjQ3OjctNDc6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NzoxNC00Nzo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ3OjMzLTQ3OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDk6MS01NDo3IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTA6My01NDo3IiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1MTo3LTUxOjEzMCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUxOjEyLTUxOjM0IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MToxMi01MToyNSIgcGF0aD0iZ2VuZGVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjEyLTUxOjE4IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTE6MjktNTE6MzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUxOjQxLTUxOjEzMCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6NTQtNTE6NTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJNIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1MTo2Ny01MToxMTEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6MTIzLTUxOjEyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ik1hbGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTI6Ny01MjoxMzQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MjoxMi01MjozNiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTItNTI6MjUiIHBhdGg9ImdlbmRlciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MjoxMi01MjoxOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjI5LTUyOjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmVtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUyOjQzLTUyOjEzNCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6NTYtNTI6NTgiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1Mjo2OS01MjoxMTMiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6MTI1LTUyOjEzMiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZlbWFsZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxMi01MzoxNSIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiU0RFIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVwZGF0ZSIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6NDEiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6OTkiLAogICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZGVmYXVsdCIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo0MC0xNzo5OSIsCiAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NDktMTc6NzIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NzUtMTc6OTgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAidmFsdWVTZXRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiRXRobmljaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo5NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJPTkMgQWRtaW5pc3RyYXRpdmUgU2V4IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDo4NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQYXllciIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlJhY2UiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExNDIyMi40LjExLjgzNiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI4OjQwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yODo0MCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjgiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjEyLTI1OjUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjoyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzo5LTIzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjE1LTIzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6MzEtMjM6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci91cy9jb3JlL1N0cnVjdHVyZURlZmluaXRpb24vdXMtY29yZS1ldGhuaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MTEtMjQ6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDoxOC0yNDozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNzoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MjEtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjIwLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODo3LTI4OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MTQtMjg6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODozMy0yODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zNzozIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTozLTM3OjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEwLTM3OjMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6NDMiLAogICAgICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db3ZlcmFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogInR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjMwLTMxOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlcnZlIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjo1LTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MTEtMzI6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMzLTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjctMzY6NyIsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjE0LTM2OjciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6OS0zNDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjktMzQ6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTEtMzU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxOC0zNTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS00Nzo0MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDozLTQ3OjQwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6OCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGbGF0dGVuIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTItNDQ6NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjktNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTUtNDI6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjozMS00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MTEtNDM6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxOC00MzozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6Ny00NjoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIwLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo3LTQ3OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTQtNDc6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NzozMy00NzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS01NDo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6My01NDo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo3LTUxOjEzMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjM0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTItNTE6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MToyOS01MTozNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0MS01MToxMzAiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NTQtNTE6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2Ny01MToxMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTIzLTUxOjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6Ny01MjoxMzQiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjozNiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJnZW5kZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEyLTUyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MjktNTI6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0My01MjoxMzQiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NTYtNTI6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo2OS01MjoxMTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTI1LTUyOjEzMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGZW1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTItNTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/SDE" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.113883.3.526.3.378", - "meta": { - "profile": [ - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareablevalueset", - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-publishablevalueset", - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "shareable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", - "valueCode": "narrative" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "publishable" - }, - { - "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-inclusion", - "valueString": "Includes concepts that represent a diagnosis of pregnancy." - }, - { - "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-exclusion", - "valueString": "No exclusions." - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "executable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", - "valueCode": "executable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning", - "valueString": "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion." - } - ], - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378", - "version": "20210218", - "title": "Pregnancy", - "status": "active", - "experimental": false, - "publisher": "American Heart Association, Inc.", - "description": "The purpose of this value set is to represent concepts for diagnoses of pregnancy.", - "purpose": "This value set may use a model element related to Diagnosis.", - "expansion": { - "timestamp": "2023-01-02T15:36:29-07:00", - "contains": [ - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "10231000132102", - "display": "In-vitro fertilization pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "102872000", - "display": "Pregnancy on oral contraceptive (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "102875003", - "display": "Surrogate pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "11082009", - "display": "Abnormal pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "127364007", - "display": "Primigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "134781000119106", - "display": "High risk pregnancy due to recurrent miscarriage (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "14080002", - "display": "Illegitimate pregnancy, life event (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "14418008", - "display": "Precocious pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "16356006", - "display": "Multiple pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169561007", - "display": "Pregnant - blood test confirms (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169562000", - "display": "Pregnant - vaginal examination confirms (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169563005", - "display": "Pregnant - on history (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169564004", - "display": "Pregnant - on abdominal palpation (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169565003", - "display": "Pregnant - planned (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169566002", - "display": "Pregnancy unplanned but wanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169567006", - "display": "Pregnancy unplanned and unwanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169568001", - "display": "Unplanned pregnancy unknown if child is wanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "199064003", - "display": "Post-term pregnancy - not delivered (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "199306007", - "display": "Continuing pregnancy after abortion of one fetus or more (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "22281000119101", - "display": "Post-term pregnancy of 40 to 42 weeks (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237233002", - "display": "Concealed pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237238006", - "display": "Pregnancy with uncertain dates (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237239003", - "display": "Low risk pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237240001", - "display": "Teenage pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237241002", - "display": "Viable pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237244005", - "display": "Single pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "248985009", - "display": "Presentation of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "276367008", - "display": "Wanted pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "281307002", - "display": "Uncertain viability of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "314204000", - "display": "Early stage of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "35381000119101", - "display": "Quadruplet pregnancy with loss of one or more fetuses (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "36801000119105", - "display": "Continuing triplet pregnancy after spontaneous abortion of one or more fetuses (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "38720006", - "display": "Septuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "41587001", - "display": "Third trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "41991004", - "display": "Angiectasis pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "429187001", - "display": "Continuing pregnancy after intrauterine death of twin fetus (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "43990006", - "display": "Sextuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "442478007", - "display": "Multiple pregnancy involving intrauterine pregnancy and tubal pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "444661007", - "display": "High risk pregnancy due to history of preterm labor (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "45307008", - "display": "Extrachorial pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "457811000124103", - "display": "Normal pregnancy in primigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "457821000124106", - "display": "Normal pregnancy in multigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459167000", - "display": "Monochorionic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459169002", - "display": "Monochorionic diamniotic twin pregnancy with similar amniotic fluid volumes (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459170001", - "display": "Monochorionic diamniotic twin pregnancy with dissimilar amniotic fluid volumes (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "47200007", - "display": "High risk pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "472321009", - "display": "Continuing pregnancy after intrauterine death of one twin with intrauterine retention of dead twin (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "57630001", - "display": "First trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "58532003", - "display": "Unwanted pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "59466002", - "display": "Second trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "60810003", - "display": "Quadruplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "64254006", - "display": "Triplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "65147003", - "display": "Twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "65727000", - "display": "Intrauterine pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "713575004", - "display": "Dizygotic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "713576003", - "display": "Monozygotic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "72892002", - "display": "Normal pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "77386006", - "display": "Pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "80997009", - "display": "Quintuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "83074005", - "display": "Unplanned pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "87527008", - "display": "Term pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "90968009", - "display": "Prolonged pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "9279009", - "display": "Extra-amniotic pregnancy (finding)" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.00", - "display": "Supervision of pregnancy with history of infertility, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.01", - "display": "Supervision of pregnancy with history of infertility, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.02", - "display": "Supervision of pregnancy with history of infertility, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.03", - "display": "Supervision of pregnancy with history of infertility, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.10", - "display": "Supervision of pregnancy with history of ectopic pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.11", - "display": "Supervision of pregnancy with history of ectopic pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.12", - "display": "Supervision of pregnancy with history of ectopic pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.13", - "display": "Supervision of pregnancy with history of ectopic pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.211", - "display": "Supervision of pregnancy with history of pre-term labor, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.212", - "display": "Supervision of pregnancy with history of pre-term labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.213", - "display": "Supervision of pregnancy with history of pre-term labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.219", - "display": "Supervision of pregnancy with history of pre-term labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.291", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.292", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.293", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.299", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.30", - "display": "Supervision of pregnancy with insufficient antenatal care, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.31", - "display": "Supervision of pregnancy with insufficient antenatal care, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.32", - "display": "Supervision of pregnancy with insufficient antenatal care, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.33", - "display": "Supervision of pregnancy with insufficient antenatal care, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.40", - "display": "Supervision of pregnancy with grand multiparity, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.41", - "display": "Supervision of pregnancy with grand multiparity, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.42", - "display": "Supervision of pregnancy with grand multiparity, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.43", - "display": "Supervision of pregnancy with grand multiparity, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.511", - "display": "Supervision of elderly primigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.512", - "display": "Supervision of elderly primigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.513", - "display": "Supervision of elderly primigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.519", - "display": "Supervision of elderly primigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.521", - "display": "Supervision of elderly multigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.522", - "display": "Supervision of elderly multigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.523", - "display": "Supervision of elderly multigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.529", - "display": "Supervision of elderly multigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.611", - "display": "Supervision of young primigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.612", - "display": "Supervision of young primigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.613", - "display": "Supervision of young primigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.619", - "display": "Supervision of young primigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.621", - "display": "Supervision of young multigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.622", - "display": "Supervision of young multigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.623", - "display": "Supervision of young multigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.629", - "display": "Supervision of young multigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.70", - "display": "Supervision of high risk pregnancy due to social problems, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.71", - "display": "Supervision of high risk pregnancy due to social problems, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.72", - "display": "Supervision of high risk pregnancy due to social problems, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.73", - "display": "Supervision of high risk pregnancy due to social problems, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.811", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.812", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.813", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.819", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.821", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.822", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.823", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.829", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.891", - "display": "Supervision of other high risk pregnancies, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.892", - "display": "Supervision of other high risk pregnancies, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.893", - "display": "Supervision of other high risk pregnancies, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.899", - "display": "Supervision of other high risk pregnancies, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.90", - "display": "Supervision of high risk pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.91", - "display": "Supervision of high risk pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.92", - "display": "Supervision of high risk pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.93", - "display": "Supervision of high risk pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A0", - "display": "Supervision of pregnancy with history of molar pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A1", - "display": "Supervision of pregnancy with history of molar pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A2", - "display": "Supervision of pregnancy with history of molar pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A3", - "display": "Supervision of pregnancy with history of molar pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.011", - "display": "Pre-existing essential hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.012", - "display": "Pre-existing essential hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.013", - "display": "Pre-existing essential hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.019", - "display": "Pre-existing essential hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.111", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.112", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.113", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.119", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.211", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.212", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.213", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.219", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.311", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.312", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.313", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.319", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.411", - "display": "Pre-existing secondary hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.412", - "display": "Pre-existing secondary hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.413", - "display": "Pre-existing secondary hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.419", - "display": "Pre-existing secondary hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.911", - "display": "Unspecified pre-existing hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.912", - "display": "Unspecified pre-existing hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.913", - "display": "Unspecified pre-existing hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.919", - "display": "Unspecified pre-existing hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.1", - "display": "Pre-existing hypertension with pre-eclampsia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.2", - "display": "Pre-existing hypertension with pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.3", - "display": "Pre-existing hypertension with pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.9", - "display": "Pre-existing hypertension with pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.00", - "display": "Gestational edema, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.01", - "display": "Gestational edema, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.02", - "display": "Gestational edema, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.03", - "display": "Gestational edema, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.10", - "display": "Gestational proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.11", - "display": "Gestational proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.12", - "display": "Gestational proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.13", - "display": "Gestational proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.20", - "display": "Gestational edema with proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.21", - "display": "Gestational edema with proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.22", - "display": "Gestational edema with proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.23", - "display": "Gestational edema with proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.1", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.2", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.3", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.9", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.00", - "display": "Mild to moderate pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.02", - "display": "Mild to moderate pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.03", - "display": "Mild to moderate pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.10", - "display": "Severe pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.12", - "display": "Severe pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.13", - "display": "Severe pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.20", - "display": "HELLP syndrome (HELLP), unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.22", - "display": "HELLP syndrome (HELLP), second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.23", - "display": "HELLP syndrome (HELLP), third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.90", - "display": "Unspecified pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.92", - "display": "Unspecified pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.93", - "display": "Unspecified pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.00", - "display": "Eclampsia complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.02", - "display": "Eclampsia complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.03", - "display": "Eclampsia complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.1", - "display": "Unspecified maternal hypertension, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.2", - "display": "Unspecified maternal hypertension, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.3", - "display": "Unspecified maternal hypertension, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.9", - "display": "Unspecified maternal hypertension, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.0", - "display": "Threatened abortion" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.8", - "display": "Other hemorrhage in early pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.9", - "display": "Hemorrhage in early pregnancy, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.0", - "display": "Mild hyperemesis gravidarum" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.1", - "display": "Hyperemesis gravidarum with metabolic disturbance" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.2", - "display": "Late vomiting of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.8", - "display": "Other vomiting complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.9", - "display": "Vomiting of pregnancy, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.00", - "display": "Varicose veins of lower extremity in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.01", - "display": "Varicose veins of lower extremity in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.02", - "display": "Varicose veins of lower extremity in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.03", - "display": "Varicose veins of lower extremity in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.10", - "display": "Genital varices in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.11", - "display": "Genital varices in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.12", - "display": "Genital varices in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.13", - "display": "Genital varices in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.20", - "display": "Superficial thrombophlebitis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.21", - "display": "Superficial thrombophlebitis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.22", - "display": "Superficial thrombophlebitis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.23", - "display": "Superficial thrombophlebitis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.30", - "display": "Deep phlebothrombosis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.31", - "display": "Deep phlebothrombosis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.32", - "display": "Deep phlebothrombosis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.33", - "display": "Deep phlebothrombosis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.40", - "display": "Hemorrhoids in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.41", - "display": "Hemorrhoids in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.42", - "display": "Hemorrhoids in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.43", - "display": "Hemorrhoids in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.50", - "display": "Cerebral venous thrombosis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.51", - "display": "Cerebral venous thrombosis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.52", - "display": "Cerebral venous thrombosis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.53", - "display": "Cerebral venous thrombosis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X1", - "display": "Other venous complications in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X2", - "display": "Other venous complications in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X3", - "display": "Other venous complications in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X9", - "display": "Other venous complications in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.90", - "display": "Venous complication in pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.91", - "display": "Venous complication in pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.92", - "display": "Venous complication in pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.93", - "display": "Venous complication in pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.00", - "display": "Infections of kidney in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.01", - "display": "Infections of kidney in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.02", - "display": "Infections of kidney in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.03", - "display": "Infections of kidney in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.10", - "display": "Infections of bladder in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.11", - "display": "Infections of bladder in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.12", - "display": "Infections of bladder in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.13", - "display": "Infections of bladder in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.20", - "display": "Infections of urethra in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.21", - "display": "Infections of urethra in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.22", - "display": "Infections of urethra in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.23", - "display": "Infections of urethra in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.30", - "display": "Infections of other parts of urinary tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.31", - "display": "Infections of other parts of urinary tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.32", - "display": "Infections of other parts of urinary tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.33", - "display": "Infections of other parts of urinary tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.40", - "display": "Unspecified infection of urinary tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.41", - "display": "Unspecified infection of urinary tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.42", - "display": "Unspecified infection of urinary tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.43", - "display": "Unspecified infection of urinary tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.511", - "display": "Infections of cervix in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.512", - "display": "Infections of cervix in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.513", - "display": "Infections of cervix in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.519", - "display": "Infections of cervix in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.521", - "display": "Salpingo-oophoritis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.522", - "display": "Salpingo-oophoritis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.523", - "display": "Salpingo-oophoritis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.529", - "display": "Salpingo-oophoritis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.591", - "display": "Infection of other part of genital tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.592", - "display": "Infection of other part of genital tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.593", - "display": "Infection of other part of genital tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.599", - "display": "Infection of other part of genital tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.90", - "display": "Unspecified genitourinary tract infection in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.91", - "display": "Unspecified genitourinary tract infection in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.92", - "display": "Unspecified genitourinary tract infection in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.93", - "display": "Unspecified genitourinary tract infection in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.011", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.012", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.013", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.019", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.111", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.112", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.113", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.119", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.311", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.312", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.313", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.319", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.410", - "display": "Gestational diabetes mellitus in pregnancy, diet controlled" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.414", - "display": "Gestational diabetes mellitus in pregnancy, insulin controlled" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.415", - "display": "Gestational diabetes mellitus in pregnancy, controlled by oral hypoglycemic drugs" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.419", - "display": "Gestational diabetes mellitus in pregnancy, unspecified control" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.811", - "display": "Other pre-existing diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.812", - "display": "Other pre-existing diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.813", - "display": "Other pre-existing diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.819", - "display": "Other pre-existing diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.911", - "display": "Unspecified diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.912", - "display": "Unspecified diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.913", - "display": "Unspecified diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.919", - "display": "Unspecified diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.10", - "display": "Malnutrition in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.11", - "display": "Malnutrition in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.12", - "display": "Malnutrition in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.13", - "display": "Malnutrition in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.00", - "display": "Excessive weight gain in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.01", - "display": "Excessive weight gain in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.02", - "display": "Excessive weight gain in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.03", - "display": "Excessive weight gain in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.10", - "display": "Low weight gain in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.11", - "display": "Low weight gain in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.12", - "display": "Low weight gain in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.13", - "display": "Low weight gain in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.20", - "display": "Pregnancy care for patient with recurrent pregnancy loss, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.21", - "display": "Pregnancy care for patient with recurrent pregnancy loss, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.22", - "display": "Pregnancy care for patient with recurrent pregnancy loss, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.23", - "display": "Pregnancy care for patient with recurrent pregnancy loss, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.30", - "display": "Retained intrauterine contraceptive device in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.31", - "display": "Retained intrauterine contraceptive device in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.32", - "display": "Retained intrauterine contraceptive device in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.33", - "display": "Retained intrauterine contraceptive device in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.40", - "display": "Herpes gestationis, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.41", - "display": "Herpes gestationis, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.42", - "display": "Herpes gestationis, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.43", - "display": "Herpes gestationis, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.50", - "display": "Maternal hypotension syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.51", - "display": "Maternal hypotension syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.52", - "display": "Maternal hypotension syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.53", - "display": "Maternal hypotension syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.611", - "display": "Liver and biliary tract disorders in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.612", - "display": "Liver and biliary tract disorders in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.613", - "display": "Liver and biliary tract disorders in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.619", - "display": "Liver and biliary tract disorders in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.711", - "display": "Subluxation of symphysis (pubis) in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.712", - "display": "Subluxation of symphysis (pubis) in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.713", - "display": "Subluxation of symphysis (pubis) in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.719", - "display": "Subluxation of symphysis (pubis) in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.811", - "display": "Pregnancy related exhaustion and fatigue, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.812", - "display": "Pregnancy related exhaustion and fatigue, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.813", - "display": "Pregnancy related exhaustion and fatigue, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.819", - "display": "Pregnancy related exhaustion and fatigue, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.821", - "display": "Pregnancy related peripheral neuritis, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.822", - "display": "Pregnancy related peripheral neuritis, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.823", - "display": "Pregnancy related peripheral neuritis, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.829", - "display": "Pregnancy related peripheral neuritis, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.831", - "display": "Pregnancy related renal disease, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.832", - "display": "Pregnancy related renal disease, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.833", - "display": "Pregnancy related renal disease, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.839", - "display": "Pregnancy related renal disease, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.841", - "display": "Uterine size-date discrepancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.842", - "display": "Uterine size-date discrepancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.843", - "display": "Uterine size-date discrepancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.849", - "display": "Uterine size-date discrepancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.851", - "display": "Spotting complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.852", - "display": "Spotting complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.853", - "display": "Spotting complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.859", - "display": "Spotting complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.86", - "display": "Pruritic urticarial papules and plaques of pregnancy (PUPPP)" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.872", - "display": "Cervical shortening, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.873", - "display": "Cervical shortening, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.879", - "display": "Cervical shortening, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.891", - "display": "Other specified pregnancy related conditions, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.892", - "display": "Other specified pregnancy related conditions, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.893", - "display": "Other specified pregnancy related conditions, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.899", - "display": "Other specified pregnancy related conditions, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.90", - "display": "Pregnancy related conditions, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.91", - "display": "Pregnancy related conditions, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.92", - "display": "Pregnancy related conditions, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.93", - "display": "Pregnancy related conditions, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.0", - "display": "Abnormal hematological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.1", - "display": "Abnormal biochemical finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.2", - "display": "Abnormal cytological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.3", - "display": "Abnormal ultrasonic finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.4", - "display": "Abnormal radiological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.5", - "display": "Abnormal chromosomal and genetic finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.8", - "display": "Other abnormal findings on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.9", - "display": "Unspecified abnormal findings on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.011", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.012", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.013", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.019", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.021", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.022", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.023", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.029", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.091", - "display": "Other pulmonary complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.092", - "display": "Other pulmonary complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.093", - "display": "Other pulmonary complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.099", - "display": "Other pulmonary complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.111", - "display": "Cardiac arrest due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.112", - "display": "Cardiac arrest due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.113", - "display": "Cardiac arrest due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.119", - "display": "Cardiac arrest due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.121", - "display": "Cardiac failure due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.122", - "display": "Cardiac failure due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.123", - "display": "Cardiac failure due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.129", - "display": "Cardiac failure due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.191", - "display": "Other cardiac complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.192", - "display": "Other cardiac complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.193", - "display": "Other cardiac complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.199", - "display": "Other cardiac complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.211", - "display": "Cerebral anoxia due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.212", - "display": "Cerebral anoxia due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.213", - "display": "Cerebral anoxia due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.219", - "display": "Cerebral anoxia due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.291", - "display": "Other central nervous system complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.292", - "display": "Other central nervous system complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.293", - "display": "Other central nervous system complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.299", - "display": "Other central nervous system complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X1", - "display": "Toxic reaction to local anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X2", - "display": "Toxic reaction to local anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X3", - "display": "Toxic reaction to local anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X9", - "display": "Toxic reaction to local anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.40", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.41", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.42", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.43", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X1", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X2", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X3", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X9", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.60", - "display": "Failed or difficult intubation for anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.61", - "display": "Failed or difficult intubation for anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.62", - "display": "Failed or difficult intubation for anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.63", - "display": "Failed or difficult intubation for anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X1", - "display": "Other complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X2", - "display": "Other complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X3", - "display": "Other complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X9", - "display": "Other complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.90", - "display": "Unspecified complication of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.91", - "display": "Unspecified complication of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.92", - "display": "Unspecified complication of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.93", - "display": "Unspecified complication of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.001", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.002", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.003", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.009", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.011", - "display": "Twin pregnancy, monochorionic/monoamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.012", - "display": "Twin pregnancy, monochorionic/monoamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.013", - "display": "Twin pregnancy, monochorionic/monoamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.019", - "display": "Twin pregnancy, monochorionic/monoamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.021", - "display": "Conjoined twin pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.022", - "display": "Conjoined twin pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.023", - "display": "Conjoined twin pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.029", - "display": "Conjoined twin pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.031", - "display": "Twin pregnancy, monochorionic/diamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.032", - "display": "Twin pregnancy, monochorionic/diamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.033", - "display": "Twin pregnancy, monochorionic/diamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.039", - "display": "Twin pregnancy, monochorionic/diamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.041", - "display": "Twin pregnancy, dichorionic/diamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.042", - "display": "Twin pregnancy, dichorionic/diamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.043", - "display": "Twin pregnancy, dichorionic/diamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.049", - "display": "Twin pregnancy, dichorionic/diamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.091", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.092", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.093", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.099", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.101", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.102", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.103", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.109", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.111", - "display": "Triplet pregnancy with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.112", - "display": "Triplet pregnancy with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.113", - "display": "Triplet pregnancy with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.119", - "display": "Triplet pregnancy with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.121", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.122", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.123", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.129", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.131", - "display": "Triplet pregnancy, trichorionic/triamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.132", - "display": "Triplet pregnancy, trichorionic/triamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.133", - "display": "Triplet pregnancy, trichorionic/triamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.139", - "display": "Triplet pregnancy, trichorionic/triamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.191", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.192", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.193", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.199", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.201", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.202", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.203", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.209", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.211", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.212", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.213", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.219", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.221", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.222", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.223", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.229", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.231", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.232", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.233", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.239", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.291", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.292", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.293", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.299", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.801", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.802", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.803", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.809", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.811", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.812", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.813", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.819", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.821", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.822", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.823", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.829", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.831", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.832", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.833", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.839", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.891", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.892", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.893", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.899", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.90", - "display": "Multiple gestation, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.91", - "display": "Multiple gestation, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.92", - "display": "Multiple gestation, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.93", - "display": "Multiple gestation, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X0", - "display": "Papyraceous fetus, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X1", - "display": "Papyraceous fetus, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X2", - "display": "Papyraceous fetus, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X3", - "display": "Papyraceous fetus, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X4", - "display": "Papyraceous fetus, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X5", - "display": "Papyraceous fetus, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X9", - "display": "Papyraceous fetus, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X0", - "display": "Papyraceous fetus, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X1", - "display": "Papyraceous fetus, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X2", - "display": "Papyraceous fetus, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X3", - "display": "Papyraceous fetus, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X4", - "display": "Papyraceous fetus, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X5", - "display": "Papyraceous fetus, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X9", - "display": "Papyraceous fetus, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X0", - "display": "Papyraceous fetus, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X1", - "display": "Papyraceous fetus, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X2", - "display": "Papyraceous fetus, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X3", - "display": "Papyraceous fetus, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X4", - "display": "Papyraceous fetus, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X5", - "display": "Papyraceous fetus, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X9", - "display": "Papyraceous fetus, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X0", - "display": "Papyraceous fetus, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X1", - "display": "Papyraceous fetus, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X2", - "display": "Papyraceous fetus, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X3", - "display": "Papyraceous fetus, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X4", - "display": "Papyraceous fetus, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X5", - "display": "Papyraceous fetus, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X9", - "display": "Papyraceous fetus, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X10", - "display": "Other complications specific to multiple gestation, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X11", - "display": "Other complications specific to multiple gestation, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X12", - "display": "Other complications specific to multiple gestation, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X13", - "display": "Other complications specific to multiple gestation, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X14", - "display": "Other complications specific to multiple gestation, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X15", - "display": "Other complications specific to multiple gestation, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X19", - "display": "Other complications specific to multiple gestation, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X20", - "display": "Other complications specific to multiple gestation, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X21", - "display": "Other complications specific to multiple gestation, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X22", - "display": "Other complications specific to multiple gestation, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X23", - "display": "Other complications specific to multiple gestation, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X24", - "display": "Other complications specific to multiple gestation, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X25", - "display": "Other complications specific to multiple gestation, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X29", - "display": "Other complications specific to multiple gestation, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X30", - "display": "Other complications specific to multiple gestation, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X31", - "display": "Other complications specific to multiple gestation, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X32", - "display": "Other complications specific to multiple gestation, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X33", - "display": "Other complications specific to multiple gestation, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X34", - "display": "Other complications specific to multiple gestation, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X35", - "display": "Other complications specific to multiple gestation, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X39", - "display": "Other complications specific to multiple gestation, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X90", - "display": "Other complications specific to multiple gestation, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X91", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X92", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X93", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X94", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X95", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X99", - "display": "Other complications specific to multiple gestation, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX0", - "display": "Maternal care for unstable lie, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX1", - "display": "Maternal care for unstable lie, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX2", - "display": "Maternal care for unstable lie, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX3", - "display": "Maternal care for unstable lie, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX4", - "display": "Maternal care for unstable lie, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX5", - "display": "Maternal care for unstable lie, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX9", - "display": "Maternal care for unstable lie, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX0", - "display": "Maternal care for breech presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX1", - "display": "Maternal care for breech presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX2", - "display": "Maternal care for breech presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX3", - "display": "Maternal care for breech presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX4", - "display": "Maternal care for breech presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX5", - "display": "Maternal care for breech presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX9", - "display": "Maternal care for breech presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX0", - "display": "Maternal care for transverse and oblique lie, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX1", - "display": "Maternal care for transverse and oblique lie, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX2", - "display": "Maternal care for transverse and oblique lie, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX3", - "display": "Maternal care for transverse and oblique lie, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX4", - "display": "Maternal care for transverse and oblique lie, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX5", - "display": "Maternal care for transverse and oblique lie, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX9", - "display": "Maternal care for transverse and oblique lie, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX0", - "display": "Maternal care for face, brow and chin presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX1", - "display": "Maternal care for face, brow and chin presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX2", - "display": "Maternal care for face, brow and chin presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX3", - "display": "Maternal care for face, brow and chin presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX4", - "display": "Maternal care for face, brow and chin presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX5", - "display": "Maternal care for face, brow and chin presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX9", - "display": "Maternal care for face, brow and chin presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX0", - "display": "Maternal care for high head at term, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX1", - "display": "Maternal care for high head at term, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX2", - "display": "Maternal care for high head at term, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX3", - "display": "Maternal care for high head at term, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX4", - "display": "Maternal care for high head at term, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX5", - "display": "Maternal care for high head at term, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX9", - "display": "Maternal care for high head at term, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX0", - "display": "Maternal care for compound presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX1", - "display": "Maternal care for compound presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX2", - "display": "Maternal care for compound presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX3", - "display": "Maternal care for compound presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX4", - "display": "Maternal care for compound presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX5", - "display": "Maternal care for compound presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX9", - "display": "Maternal care for compound presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX0", - "display": "Maternal care for other malpresentation of fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX1", - "display": "Maternal care for other malpresentation of fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX2", - "display": "Maternal care for other malpresentation of fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX3", - "display": "Maternal care for other malpresentation of fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX4", - "display": "Maternal care for other malpresentation of fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX5", - "display": "Maternal care for other malpresentation of fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX9", - "display": "Maternal care for other malpresentation of fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX0", - "display": "Maternal care for malpresentation of fetus, unspecified, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX1", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX2", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX3", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX4", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX5", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX9", - "display": "Maternal care for malpresentation of fetus, unspecified, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.0", - "display": "Maternal care for disproportion due to deformity of maternal pelvic bones" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.1", - "display": "Maternal care for disproportion due to generally contracted pelvis" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.2", - "display": "Maternal care for disproportion due to inlet contraction of pelvis" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX0", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX1", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX2", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX3", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX4", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX5", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX9", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX0", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX1", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX2", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX3", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX4", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX5", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX9", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX0", - "display": "Maternal care for disproportion due to unusually large fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX1", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX2", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX3", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX4", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX5", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX9", - "display": "Maternal care for disproportion due to unusually large fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX0", - "display": "Maternal care for disproportion due to hydrocephalic fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX1", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX2", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX3", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX4", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX5", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX9", - "display": "Maternal care for disproportion due to hydrocephalic fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX0", - "display": "Maternal care for disproportion due to other fetal deformities, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX1", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX2", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX3", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX4", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX5", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX9", - "display": "Maternal care for disproportion due to other fetal deformities, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.8", - "display": "Maternal care for disproportion of other origin" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.9", - "display": "Maternal care for disproportion, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.00", - "display": "Maternal care for unspecified congenital malformation of uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.01", - "display": "Maternal care for unspecified congenital malformation of uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.02", - "display": "Maternal care for unspecified congenital malformation of uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.03", - "display": "Maternal care for unspecified congenital malformation of uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.10", - "display": "Maternal care for benign tumor of corpus uteri, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.11", - "display": "Maternal care for benign tumor of corpus uteri, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.12", - "display": "Maternal care for benign tumor of corpus uteri, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.13", - "display": "Maternal care for benign tumor of corpus uteri, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.211", - "display": "Maternal care for low transverse scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.212", - "display": "Maternal care for vertical scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.219", - "display": "Maternal care for unspecified type scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.29", - "display": "Maternal care due to uterine scar from other previous surgery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.30", - "display": "Maternal care for cervical incompetence, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.31", - "display": "Maternal care for cervical incompetence, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.32", - "display": "Maternal care for cervical incompetence, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.33", - "display": "Maternal care for cervical incompetence, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.40", - "display": "Maternal care for other abnormalities of cervix, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.41", - "display": "Maternal care for other abnormalities of cervix, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.42", - "display": "Maternal care for other abnormalities of cervix, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.43", - "display": "Maternal care for other abnormalities of cervix, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.511", - "display": "Maternal care for incarceration of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.512", - "display": "Maternal care for incarceration of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.513", - "display": "Maternal care for incarceration of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.519", - "display": "Maternal care for incarceration of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.521", - "display": "Maternal care for prolapse of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.522", - "display": "Maternal care for prolapse of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.523", - "display": "Maternal care for prolapse of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.529", - "display": "Maternal care for prolapse of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.531", - "display": "Maternal care for retroversion of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.532", - "display": "Maternal care for retroversion of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.533", - "display": "Maternal care for retroversion of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.539", - "display": "Maternal care for retroversion of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.591", - "display": "Maternal care for other abnormalities of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.592", - "display": "Maternal care for other abnormalities of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.593", - "display": "Maternal care for other abnormalities of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.599", - "display": "Maternal care for other abnormalities of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.60", - "display": "Maternal care for abnormality of vagina, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.61", - "display": "Maternal care for abnormality of vagina, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.62", - "display": "Maternal care for abnormality of vagina, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.63", - "display": "Maternal care for abnormality of vagina, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.70", - "display": "Maternal care for abnormality of vulva and perineum, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.71", - "display": "Maternal care for abnormality of vulva and perineum, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.72", - "display": "Maternal care for abnormality of vulva and perineum, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.73", - "display": "Maternal care for abnormality of vulva and perineum, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.80", - "display": "Maternal care for other abnormalities of pelvic organs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.81", - "display": "Maternal care for other abnormalities of pelvic organs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.82", - "display": "Maternal care for other abnormalities of pelvic organs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.83", - "display": "Maternal care for other abnormalities of pelvic organs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.90", - "display": "Maternal care for abnormality of pelvic organ, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.91", - "display": "Maternal care for abnormality of pelvic organ, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.92", - "display": "Maternal care for abnormality of pelvic organ, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.93", - "display": "Maternal care for abnormality of pelvic organ, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX0", - "display": "Maternal care for (suspected) hereditary disease in fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX1", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX2", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX3", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX4", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX5", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX9", - "display": "Maternal care for (suspected) hereditary disease in fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX0", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX1", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX2", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX3", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX4", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX5", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX9", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX0", - "display": "Maternal care for (suspected) damage to fetus from alcohol, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX1", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX2", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX3", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX4", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX5", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX9", - "display": "Maternal care for (suspected) damage to fetus from alcohol, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX0", - "display": "Maternal care for (suspected) damage to fetus by drugs, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX1", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX2", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX3", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX4", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX5", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX9", - "display": "Maternal care for (suspected) damage to fetus by drugs, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX0", - "display": "Maternal care for (suspected) damage to fetus by radiation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX1", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX2", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX3", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX4", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX5", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX9", - "display": "Maternal care for (suspected) damage to fetus by radiation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX0", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX1", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX2", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX3", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX4", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX5", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX9", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX0", - "display": "Maternal care for other (suspected) fetal abnormality and damage, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX1", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX2", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX3", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX4", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX5", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX9", - "display": "Maternal care for other (suspected) fetal abnormality and damage, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX0", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX1", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX2", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX3", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX4", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX5", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX9", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0110", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0111", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0112", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0113", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0114", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0115", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0119", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0120", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0121", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0122", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0123", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0124", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0125", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0129", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0130", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0131", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0132", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0133", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0134", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0135", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0139", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0190", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0191", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0192", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0193", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0194", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0195", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0199", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0910", - "display": "Maternal care for other rhesus isoimmunization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0911", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0912", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0913", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0914", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0915", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0919", - "display": "Maternal care for other rhesus isoimmunization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0920", - "display": "Maternal care for other rhesus isoimmunization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0921", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0922", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0923", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0924", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0925", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0929", - "display": "Maternal care for other rhesus isoimmunization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0930", - "display": "Maternal care for other rhesus isoimmunization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0931", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0932", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0933", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0934", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0935", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0939", - "display": "Maternal care for other rhesus isoimmunization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0990", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0991", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0992", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0993", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0994", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0995", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0999", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1110", - "display": "Maternal care for Anti-A sensitization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1111", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1112", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1113", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1114", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1115", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1119", - "display": "Maternal care for Anti-A sensitization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1120", - "display": "Maternal care for Anti-A sensitization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1121", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1122", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1123", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1124", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1125", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1129", - "display": "Maternal care for Anti-A sensitization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1130", - "display": "Maternal care for Anti-A sensitization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1131", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1132", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1133", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1134", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1135", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1139", - "display": "Maternal care for Anti-A sensitization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1190", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1191", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1192", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1193", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1194", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1195", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1199", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1910", - "display": "Maternal care for other isoimmunization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1911", - "display": "Maternal care for other isoimmunization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1912", - "display": "Maternal care for other isoimmunization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1913", - "display": "Maternal care for other isoimmunization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1914", - "display": "Maternal care for other isoimmunization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1915", - "display": "Maternal care for other isoimmunization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1919", - "display": "Maternal care for other isoimmunization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1920", - "display": "Maternal care for other isoimmunization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1921", - "display": "Maternal care for other isoimmunization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1922", - "display": "Maternal care for other isoimmunization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1923", - "display": "Maternal care for other isoimmunization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1924", - "display": "Maternal care for other isoimmunization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1925", - "display": "Maternal care for other isoimmunization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1929", - "display": "Maternal care for other isoimmunization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1930", - "display": "Maternal care for other isoimmunization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1931", - "display": "Maternal care for other isoimmunization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1932", - "display": "Maternal care for other isoimmunization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1933", - "display": "Maternal care for other isoimmunization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1934", - "display": "Maternal care for other isoimmunization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1935", - "display": "Maternal care for other isoimmunization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1939", - "display": "Maternal care for other isoimmunization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1990", - "display": "Maternal care for other isoimmunization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1991", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1992", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1993", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1994", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1995", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1999", - "display": "Maternal care for other isoimmunization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X0", - "display": "Maternal care for hydrops fetalis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X1", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X2", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X3", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X4", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X5", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X9", - "display": "Maternal care for hydrops fetalis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X0", - "display": "Maternal care for hydrops fetalis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X1", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X2", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X3", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X4", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X5", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X9", - "display": "Maternal care for hydrops fetalis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X0", - "display": "Maternal care for hydrops fetalis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X1", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X2", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X3", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X4", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X5", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X9", - "display": "Maternal care for hydrops fetalis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X0", - "display": "Maternal care for hydrops fetalis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X1", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X2", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X3", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X4", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X5", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X9", - "display": "Maternal care for hydrops fetalis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX0", - "display": "Maternal care for intrauterine death, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX1", - "display": "Maternal care for intrauterine death, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX2", - "display": "Maternal care for intrauterine death, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX3", - "display": "Maternal care for intrauterine death, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX4", - "display": "Maternal care for intrauterine death, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX5", - "display": "Maternal care for intrauterine death, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX9", - "display": "Maternal care for intrauterine death, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5110", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5111", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5112", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5113", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5114", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5115", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5119", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5120", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5121", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5122", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5123", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5124", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5125", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5129", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5130", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5131", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5132", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5133", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5134", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5135", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5139", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5190", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5191", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5192", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5193", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5194", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5195", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5199", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5910", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5911", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5912", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5913", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5914", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5915", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5919", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5920", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5921", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5922", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5923", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5924", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5925", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5929", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5930", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5931", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5932", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5933", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5934", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5935", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5939", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5990", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5991", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5992", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5993", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5994", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5995", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5999", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X0", - "display": "Maternal care for excessive fetal growth, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X1", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X2", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X3", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X4", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X5", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X9", - "display": "Maternal care for excessive fetal growth, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X0", - "display": "Maternal care for excessive fetal growth, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X1", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X2", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X3", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X4", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X5", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X9", - "display": "Maternal care for excessive fetal growth, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X0", - "display": "Maternal care for excessive fetal growth, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X1", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X2", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X3", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X4", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X5", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X9", - "display": "Maternal care for excessive fetal growth, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X0", - "display": "Maternal care for excessive fetal growth, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X1", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X2", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X3", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X4", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X5", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X9", - "display": "Maternal care for excessive fetal growth, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8120", - "display": "Decreased fetal movements, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8121", - "display": "Decreased fetal movements, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8122", - "display": "Decreased fetal movements, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8123", - "display": "Decreased fetal movements, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8124", - "display": "Decreased fetal movements, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8125", - "display": "Decreased fetal movements, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8129", - "display": "Decreased fetal movements, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8130", - "display": "Decreased fetal movements, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8131", - "display": "Decreased fetal movements, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8132", - "display": "Decreased fetal movements, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8133", - "display": "Decreased fetal movements, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8134", - "display": "Decreased fetal movements, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8135", - "display": "Decreased fetal movements, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8139", - "display": "Decreased fetal movements, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8190", - "display": "Decreased fetal movements, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8191", - "display": "Decreased fetal movements, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8192", - "display": "Decreased fetal movements, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8193", - "display": "Decreased fetal movements, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8194", - "display": "Decreased fetal movements, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8195", - "display": "Decreased fetal movements, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8199", - "display": "Decreased fetal movements, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8210", - "display": "Fetal anemia and thrombocytopenia, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8211", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8212", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8213", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8214", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8215", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8219", - "display": "Fetal anemia and thrombocytopenia, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8220", - "display": "Fetal anemia and thrombocytopenia, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8221", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8222", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8223", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8224", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8225", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8229", - "display": "Fetal anemia and thrombocytopenia, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8230", - "display": "Fetal anemia and thrombocytopenia, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8231", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8232", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8233", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8234", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8235", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8239", - "display": "Fetal anemia and thrombocytopenia, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8290", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8291", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8292", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8293", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8294", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8295", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8299", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8310", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8311", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8312", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8313", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8314", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8315", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8319", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8320", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8321", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8322", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8323", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8324", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8325", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8329", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8330", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8331", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8332", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8333", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8334", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8335", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8339", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8390", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8391", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8392", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8393", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8394", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8395", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8399", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8910", - "display": "Maternal care for other specified fetal problems, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8911", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8912", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8913", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8914", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8915", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8919", - "display": "Maternal care for other specified fetal problems, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8920", - "display": "Maternal care for other specified fetal problems, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8921", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8922", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8923", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8924", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8925", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8929", - "display": "Maternal care for other specified fetal problems, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8930", - "display": "Maternal care for other specified fetal problems, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8931", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8932", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8933", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8934", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8935", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8939", - "display": "Maternal care for other specified fetal problems, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8990", - "display": "Maternal care for other specified fetal problems, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8991", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8992", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8993", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8994", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8995", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8999", - "display": "Maternal care for other specified fetal problems, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X0", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X1", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X2", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X3", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X4", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X5", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X9", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X0", - "display": "Maternal care for fetal problem, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X1", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X2", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X3", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X4", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X5", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X9", - "display": "Maternal care for fetal problem, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X0", - "display": "Maternal care for fetal problem, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X1", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X2", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X3", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X4", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X5", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X9", - "display": "Maternal care for fetal problem, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X0", - "display": "Maternal care for fetal problem, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X1", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X2", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X3", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X4", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X5", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X9", - "display": "Maternal care for fetal problem, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX0", - "display": "Polyhydramnios, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX1", - "display": "Polyhydramnios, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX2", - "display": "Polyhydramnios, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX3", - "display": "Polyhydramnios, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX4", - "display": "Polyhydramnios, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX5", - "display": "Polyhydramnios, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX9", - "display": "Polyhydramnios, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX0", - "display": "Polyhydramnios, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX1", - "display": "Polyhydramnios, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX2", - "display": "Polyhydramnios, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX3", - "display": "Polyhydramnios, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX4", - "display": "Polyhydramnios, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX5", - "display": "Polyhydramnios, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX9", - "display": "Polyhydramnios, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX0", - "display": "Polyhydramnios, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX1", - "display": "Polyhydramnios, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX2", - "display": "Polyhydramnios, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX3", - "display": "Polyhydramnios, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX4", - "display": "Polyhydramnios, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX5", - "display": "Polyhydramnios, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX9", - "display": "Polyhydramnios, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX0", - "display": "Polyhydramnios, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX1", - "display": "Polyhydramnios, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX2", - "display": "Polyhydramnios, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX3", - "display": "Polyhydramnios, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX4", - "display": "Polyhydramnios, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX5", - "display": "Polyhydramnios, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX9", - "display": "Polyhydramnios, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X0", - "display": "Oligohydramnios, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X1", - "display": "Oligohydramnios, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X2", - "display": "Oligohydramnios, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X3", - "display": "Oligohydramnios, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X4", - "display": "Oligohydramnios, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X5", - "display": "Oligohydramnios, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X9", - "display": "Oligohydramnios, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X0", - "display": "Oligohydramnios, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X1", - "display": "Oligohydramnios, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X2", - "display": "Oligohydramnios, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X3", - "display": "Oligohydramnios, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X4", - "display": "Oligohydramnios, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X5", - "display": "Oligohydramnios, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X9", - "display": "Oligohydramnios, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X0", - "display": "Oligohydramnios, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X1", - "display": "Oligohydramnios, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X2", - "display": "Oligohydramnios, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X3", - "display": "Oligohydramnios, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X4", - "display": "Oligohydramnios, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X5", - "display": "Oligohydramnios, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X9", - "display": "Oligohydramnios, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X0", - "display": "Oligohydramnios, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X1", - "display": "Oligohydramnios, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X2", - "display": "Oligohydramnios, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X3", - "display": "Oligohydramnios, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X4", - "display": "Oligohydramnios, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X5", - "display": "Oligohydramnios, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X9", - "display": "Oligohydramnios, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1010", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1011", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1012", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1013", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1014", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1015", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1019", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1020", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1021", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1022", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1023", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1024", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1025", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1029", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1030", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1031", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1032", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1033", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1034", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1035", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1039", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1090", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1091", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1092", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1093", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1094", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1095", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1099", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1210", - "display": "Chorioamnionitis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1211", - "display": "Chorioamnionitis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1212", - "display": "Chorioamnionitis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1213", - "display": "Chorioamnionitis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1214", - "display": "Chorioamnionitis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1215", - "display": "Chorioamnionitis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1219", - "display": "Chorioamnionitis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1220", - "display": "Chorioamnionitis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1221", - "display": "Chorioamnionitis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1222", - "display": "Chorioamnionitis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1223", - "display": "Chorioamnionitis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1224", - "display": "Chorioamnionitis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1225", - "display": "Chorioamnionitis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1229", - "display": "Chorioamnionitis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1230", - "display": "Chorioamnionitis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1231", - "display": "Chorioamnionitis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1232", - "display": "Chorioamnionitis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1233", - "display": "Chorioamnionitis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1234", - "display": "Chorioamnionitis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1235", - "display": "Chorioamnionitis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1239", - "display": "Chorioamnionitis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1290", - "display": "Chorioamnionitis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1291", - "display": "Chorioamnionitis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1292", - "display": "Chorioamnionitis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1293", - "display": "Chorioamnionitis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1294", - "display": "Chorioamnionitis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1295", - "display": "Chorioamnionitis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1299", - "display": "Chorioamnionitis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1410", - "display": "Placentitis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1411", - "display": "Placentitis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1412", - "display": "Placentitis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1413", - "display": "Placentitis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1414", - "display": "Placentitis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1415", - "display": "Placentitis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1419", - "display": "Placentitis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1420", - "display": "Placentitis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1421", - "display": "Placentitis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1422", - "display": "Placentitis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1423", - "display": "Placentitis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1424", - "display": "Placentitis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1425", - "display": "Placentitis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1429", - "display": "Placentitis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1430", - "display": "Placentitis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1431", - "display": "Placentitis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1432", - "display": "Placentitis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1433", - "display": "Placentitis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1434", - "display": "Placentitis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1435", - "display": "Placentitis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1439", - "display": "Placentitis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1490", - "display": "Placentitis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1491", - "display": "Placentitis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1492", - "display": "Placentitis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1493", - "display": "Placentitis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1494", - "display": "Placentitis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1495", - "display": "Placentitis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1499", - "display": "Placentitis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X10", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X11", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X12", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X13", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X14", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X15", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X19", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X20", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X21", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X22", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X23", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X24", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X25", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X29", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X30", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X31", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X32", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X33", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X34", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X35", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X39", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X90", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X91", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X92", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X93", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X94", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X95", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X99", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.00", - "display": "Premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.011", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.012", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.013", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.019", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.02", - "display": "Full-term premature rupture of membranes, onset of labor within 24 hours of rupture" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.10", - "display": "Premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.111", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.112", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.113", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.119", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.12", - "display": "Full-term premature rupture of membranes, onset of labor more than 24 hours following rupture" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.90", - "display": "Premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.911", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.912", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.913", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.919", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.92", - "display": "Full-term premature rupture of membranes, unspecified as to length of time between rupture and onset of labor" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.011", - "display": "Fetomaternal placental transfusion syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.012", - "display": "Fetomaternal placental transfusion syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.013", - "display": "Fetomaternal placental transfusion syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.019", - "display": "Fetomaternal placental transfusion syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.021", - "display": "Fetus-to-fetus placental transfusion syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.022", - "display": "Fetus-to-fetus placental transfusion syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.023", - "display": "Fetus-to-fetus placental transfusion syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.029", - "display": "Fetus-to-fetus placental transfusion syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.101", - "display": "Malformation of placenta, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.102", - "display": "Malformation of placenta, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.103", - "display": "Malformation of placenta, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.109", - "display": "Malformation of placenta, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.111", - "display": "Circumvallate placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.112", - "display": "Circumvallate placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.113", - "display": "Circumvallate placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.119", - "display": "Circumvallate placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.121", - "display": "Velamentous insertion of umbilical cord, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.122", - "display": "Velamentous insertion of umbilical cord, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.123", - "display": "Velamentous insertion of umbilical cord, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.129", - "display": "Velamentous insertion of umbilical cord, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.191", - "display": "Other malformation of placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.192", - "display": "Other malformation of placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.193", - "display": "Other malformation of placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.199", - "display": "Other malformation of placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.211", - "display": "Placenta accreta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.212", - "display": "Placenta accreta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.213", - "display": "Placenta accreta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.219", - "display": "Placenta accreta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.221", - "display": "Placenta increta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.222", - "display": "Placenta increta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.223", - "display": "Placenta increta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.229", - "display": "Placenta increta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.231", - "display": "Placenta percreta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.232", - "display": "Placenta percreta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.233", - "display": "Placenta percreta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.239", - "display": "Placenta percreta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.811", - "display": "Placental infarction, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.812", - "display": "Placental infarction, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.813", - "display": "Placental infarction, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.819", - "display": "Placental infarction, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.891", - "display": "Other placental disorders, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.892", - "display": "Other placental disorders, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.893", - "display": "Other placental disorders, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.899", - "display": "Other placental disorders, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.90", - "display": "Unspecified placental disorder, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.91", - "display": "Unspecified placental disorder, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.92", - "display": "Unspecified placental disorder, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.93", - "display": "Unspecified placental disorder, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.00", - "display": "Complete placenta previa NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.01", - "display": "Complete placenta previa NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.02", - "display": "Complete placenta previa NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.03", - "display": "Complete placenta previa NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.10", - "display": "Complete placenta previa with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.11", - "display": "Complete placenta previa with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.12", - "display": "Complete placenta previa with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.13", - "display": "Complete placenta previa with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.20", - "display": "Partial placenta previa NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.21", - "display": "Partial placenta previa NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.22", - "display": "Partial placenta previa NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.23", - "display": "Partial placenta previa NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.30", - "display": "Partial placenta previa with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.31", - "display": "Partial placenta previa with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.32", - "display": "Partial placenta previa with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.33", - "display": "Partial placenta previa with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.40", - "display": "Low lying placenta NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.41", - "display": "Low lying placenta NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.42", - "display": "Low lying placenta NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.43", - "display": "Low lying placenta NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.50", - "display": "Low lying placenta with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.51", - "display": "Low lying placenta with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.52", - "display": "Low lying placenta with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.53", - "display": "Low lying placenta with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.001", - "display": "Premature separation of placenta with coagulation defect, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.002", - "display": "Premature separation of placenta with coagulation defect, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.003", - "display": "Premature separation of placenta with coagulation defect, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.009", - "display": "Premature separation of placenta with coagulation defect, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.011", - "display": "Premature separation of placenta with afibrinogenemia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.012", - "display": "Premature separation of placenta with afibrinogenemia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.013", - "display": "Premature separation of placenta with afibrinogenemia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.019", - "display": "Premature separation of placenta with afibrinogenemia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.021", - "display": "Premature separation of placenta with disseminated intravascular coagulation, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.022", - "display": "Premature separation of placenta with disseminated intravascular coagulation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.023", - "display": "Premature separation of placenta with disseminated intravascular coagulation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.029", - "display": "Premature separation of placenta with disseminated intravascular coagulation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.091", - "display": "Premature separation of placenta with other coagulation defect, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.092", - "display": "Premature separation of placenta with other coagulation defect, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.093", - "display": "Premature separation of placenta with other coagulation defect, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.099", - "display": "Premature separation of placenta with other coagulation defect, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X1", - "display": "Other premature separation of placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X2", - "display": "Other premature separation of placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X3", - "display": "Other premature separation of placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X9", - "display": "Other premature separation of placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.90", - "display": "Premature separation of placenta, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.91", - "display": "Premature separation of placenta, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.92", - "display": "Premature separation of placenta, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.93", - "display": "Premature separation of placenta, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.001", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.002", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.003", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.009", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.011", - "display": "Antepartum hemorrhage with afibrinogenemia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.012", - "display": "Antepartum hemorrhage with afibrinogenemia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.013", - "display": "Antepartum hemorrhage with afibrinogenemia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.019", - "display": "Antepartum hemorrhage with afibrinogenemia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.021", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.022", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.023", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.029", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.091", - "display": "Antepartum hemorrhage with other coagulation defect, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.092", - "display": "Antepartum hemorrhage with other coagulation defect, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.093", - "display": "Antepartum hemorrhage with other coagulation defect, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.099", - "display": "Antepartum hemorrhage with other coagulation defect, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X1", - "display": "Other antepartum hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X2", - "display": "Other antepartum hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X3", - "display": "Other antepartum hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X9", - "display": "Other antepartum hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.90", - "display": "Antepartum hemorrhage, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.91", - "display": "Antepartum hemorrhage, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.92", - "display": "Antepartum hemorrhage, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.93", - "display": "Antepartum hemorrhage, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.00", - "display": "False labor before 37 completed weeks of gestation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.02", - "display": "False labor before 37 completed weeks of gestation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.03", - "display": "False labor before 37 completed weeks of gestation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.1", - "display": "False labor at or after 37 completed weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.9", - "display": "False labor, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O48.0", - "display": "Post-term pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O48.1", - "display": "Prolonged pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.00", - "display": "Preterm labor without delivery, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.02", - "display": "Preterm labor without delivery, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.03", - "display": "Preterm labor without delivery, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.00", - "display": "Rupture of uterus before onset of labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.02", - "display": "Rupture of uterus before onset of labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.03", - "display": "Rupture of uterus before onset of labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.011", - "display": "Air embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.012", - "display": "Air embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.013", - "display": "Air embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.019", - "display": "Air embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.111", - "display": "Amniotic fluid embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.112", - "display": "Amniotic fluid embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.113", - "display": "Amniotic fluid embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.119", - "display": "Amniotic fluid embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.211", - "display": "Thromboembolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.212", - "display": "Thromboembolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.213", - "display": "Thromboembolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.219", - "display": "Thromboembolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.311", - "display": "Pyemic and septic embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.312", - "display": "Pyemic and septic embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.313", - "display": "Pyemic and septic embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.319", - "display": "Pyemic and septic embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.811", - "display": "Other embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.812", - "display": "Other embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.813", - "display": "Other embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.819", - "display": "Other embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O90.3", - "display": "Peripartum cardiomyopathy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.011", - "display": "Infection of nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.012", - "display": "Infection of nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.013", - "display": "Infection of nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.019", - "display": "Infection of nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.111", - "display": "Abscess of breast associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.112", - "display": "Abscess of breast associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.113", - "display": "Abscess of breast associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.119", - "display": "Abscess of breast associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.211", - "display": "Nonpurulent mastitis associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.212", - "display": "Nonpurulent mastitis associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.213", - "display": "Nonpurulent mastitis associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.219", - "display": "Nonpurulent mastitis associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.011", - "display": "Retracted nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.012", - "display": "Retracted nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.013", - "display": "Retracted nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.019", - "display": "Retracted nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.111", - "display": "Cracked nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.112", - "display": "Cracked nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.113", - "display": "Cracked nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.119", - "display": "Cracked nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.20", - "display": "Unspecified disorder of breast associated with pregnancy and the puerperium" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.29", - "display": "Other disorders of breast associated with pregnancy and the puerperium" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.011", - "display": "Tuberculosis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.012", - "display": "Tuberculosis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.013", - "display": "Tuberculosis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.019", - "display": "Tuberculosis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.111", - "display": "Syphilis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.112", - "display": "Syphilis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.113", - "display": "Syphilis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.119", - "display": "Syphilis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.211", - "display": "Gonorrhea complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.212", - "display": "Gonorrhea complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.213", - "display": "Gonorrhea complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.219", - "display": "Gonorrhea complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.311", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.312", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.313", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.319", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.411", - "display": "Viral hepatitis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.412", - "display": "Viral hepatitis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.413", - "display": "Viral hepatitis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.419", - "display": "Viral hepatitis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.511", - "display": "Other viral diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.512", - "display": "Other viral diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.513", - "display": "Other viral diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.519", - "display": "Other viral diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.611", - "display": "Protozoal diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.612", - "display": "Protozoal diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.613", - "display": "Protozoal diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.619", - "display": "Protozoal diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.711", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.712", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.713", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.719", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.811", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.812", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.813", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.819", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.911", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.912", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.913", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.919", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.011", - "display": "Anemia complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.012", - "display": "Anemia complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.013", - "display": "Anemia complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.019", - "display": "Anemia complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.111", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.112", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.113", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.119", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.210", - "display": "Obesity complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.211", - "display": "Obesity complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.212", - "display": "Obesity complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.213", - "display": "Obesity complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.280", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.281", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.282", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.283", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.310", - "display": "Alcohol use complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.311", - "display": "Alcohol use complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.312", - "display": "Alcohol use complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.313", - "display": "Alcohol use complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.320", - "display": "Drug use complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.321", - "display": "Drug use complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.322", - "display": "Drug use complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.323", - "display": "Drug use complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.330", - "display": "Smoking (tobacco) complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.331", - "display": "Smoking (tobacco) complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.332", - "display": "Smoking (tobacco) complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.333", - "display": "Smoking (tobacco) complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.340", - "display": "Other mental disorders complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.341", - "display": "Other mental disorders complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.342", - "display": "Other mental disorders complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.343", - "display": "Other mental disorders complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.350", - "display": "Diseases of the nervous system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.351", - "display": "Diseases of the nervous system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.352", - "display": "Diseases of the nervous system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.353", - "display": "Diseases of the nervous system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.411", - "display": "Diseases of the circulatory system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.412", - "display": "Diseases of the circulatory system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.413", - "display": "Diseases of the circulatory system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.419", - "display": "Diseases of the circulatory system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.511", - "display": "Diseases of the respiratory system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.512", - "display": "Diseases of the respiratory system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.513", - "display": "Diseases of the respiratory system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.519", - "display": "Diseases of the respiratory system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.611", - "display": "Diseases of the digestive system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.612", - "display": "Diseases of the digestive system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.613", - "display": "Diseases of the digestive system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.619", - "display": "Diseases of the digestive system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.711", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.712", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.713", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.719", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.810", - "display": "Abnormal glucose complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.820", - "display": "Streptococcus B carrier state complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.830", - "display": "Other infection carrier state complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.840", - "display": "Bariatric surgery status complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.841", - "display": "Bariatric surgery status complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.842", - "display": "Bariatric surgery status complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.843", - "display": "Bariatric surgery status complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.111", - "display": "Malignant neoplasm complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.112", - "display": "Malignant neoplasm complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.113", - "display": "Malignant neoplasm complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.119", - "display": "Malignant neoplasm complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.211", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.212", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.213", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.219", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.311", - "display": "Physical abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.312", - "display": "Physical abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.313", - "display": "Physical abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.319", - "display": "Physical abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.411", - "display": "Sexual abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.412", - "display": "Sexual abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.413", - "display": "Sexual abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.419", - "display": "Sexual abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.511", - "display": "Psychological abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.512", - "display": "Psychological abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.513", - "display": "Psychological abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.519", - "display": "Psychological abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z33.1", - "display": "Pregnant state, incidental" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z33.3", - "display": "Pregnant state, gestational carrier" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.00", - "display": "Encounter for supervision of normal first pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.01", - "display": "Encounter for supervision of normal first pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.02", - "display": "Encounter for supervision of normal first pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.03", - "display": "Encounter for supervision of normal first pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.80", - "display": "Encounter for supervision of other normal pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.81", - "display": "Encounter for supervision of other normal pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.82", - "display": "Encounter for supervision of other normal pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.83", - "display": "Encounter for supervision of other normal pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.90", - "display": "Encounter for supervision of normal pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.91", - "display": "Encounter for supervision of normal pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.92", - "display": "Encounter for supervision of normal pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.93", - "display": "Encounter for supervision of normal pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.01", - "display": "Less than 8 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.08", - "display": "8 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.09", - "display": "9 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.10", - "display": "10 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.11", - "display": "11 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.12", - "display": "12 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.13", - "display": "13 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.14", - "display": "14 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.15", - "display": "15 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.16", - "display": "16 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.17", - "display": "17 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.18", - "display": "18 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.19", - "display": "19 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.20", - "display": "20 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.21", - "display": "21 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.22", - "display": "22 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.23", - "display": "23 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.24", - "display": "24 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.25", - "display": "25 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.26", - "display": "26 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.27", - "display": "27 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.28", - "display": "28 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.29", - "display": "29 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.30", - "display": "30 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.31", - "display": "31 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.32", - "display": "32 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.33", - "display": "33 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.34", - "display": "34 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.35", - "display": "35 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.36", - "display": "36 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.37", - "display": "37 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.38", - "display": "38 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.39", - "display": "39 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.40", - "display": "40 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.41", - "display": "41 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.42", - "display": "42 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.49", - "display": "Greater than 42 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0000", - "display": "Abdominal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0001", - "display": "Abdominal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00101", - "display": "Right tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00102", - "display": "Left tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00109", - "display": "Unspecified tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00111", - "display": "Right tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00112", - "display": "Left tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00119", - "display": "Unspecified tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00201", - "display": "Right ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00202", - "display": "Left ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00209", - "display": "Unspecified ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00211", - "display": "Right ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00212", - "display": "Left ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00219", - "display": "Unspecified ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0080", - "display": "Other ectopic pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0081", - "display": "Other ectopic pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0090", - "display": "Unspecified ectopic pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0091", - "display": "Unspecified ectopic pregnancy with intrauterine pregnancy" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.113883.3.526.3.378" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "DQMFHIRHelpers", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers", - "version": "4.0.1", - "name": "DQMFHIRHelpers", - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBEUU1GSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMScKCnVzaW5nIEZISVIgdmVyc2lvbiAnNC4wLjEnCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChwZXJpb2QgRkhJUi5QZXJpb2QpOgogICAgaWYgcGVyaW9kIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvUXVhbnRpdHkocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBxdWFudGl0eS51bml0LnZhbHVlIH0KCmRlZmluZSBmdW5jdGlvbiBUb1JhdGlvKHJhdGlvIEZISVIuUmF0aW8pOgogICAgaWYgcmF0aW8gaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5SYXRpbyB7IG51bWVyYXRvcjogVG9RdWFudGl0eShyYXRpby5udW1lcmF0b3IpLCBkZW5vbWluYXRvcjogVG9RdWFudGl0eShyYXRpby5kZW5vbWluYXRvcikgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocmFuZ2UgRkhJUi5SYW5nZSk6CiAgICBpZiByYW5nZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgSW50ZXJ2YWxbVG9RdWFudGl0eShyYW5nZS5sb3cpLCBUb1F1YW50aXR5KHJhbmdlLmhpZ2gpXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29kZShjb2RpbmcgRkhJUi5Db2RpbmcpOgogICAgaWYgY29kaW5nIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uQ29kZSB7CiAgICAgICAgICBjb2RlOiBjb2RpbmcuY29kZS52YWx1ZSwKICAgICAgICAgIHN5c3RlbTogY29kaW5nLnN5c3RlbS52YWx1ZSwKICAgICAgICAgIHZlcnNpb246IGNvZGluZy52ZXJzaW9uLnZhbHVlLAogICAgICAgICAgZGlzcGxheTogY29kaW5nLmRpc3BsYXkudmFsdWUKICAgICAgICB9CgpkZWZpbmUgZnVuY3Rpb24gVG9Db25jZXB0KGNvbmNlcHQgRkhJUi5Db2RlYWJsZUNvbmNlcHQpOgogICAgaWYgY29uY2VwdCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvbmNlcHQgewogICAgICAgICAgICBjb2RlczogY29uY2VwdC5jb2RpbmcgQyByZXR1cm4gVG9Db2RlKEMpLAogICAgICAgICAgICBkaXNwbGF5OiBjb25jZXB0LnRleHQudmFsdWUKICAgICAgICB9CgoKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjY291bnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ2FyZGluYWxpdHlCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Db25kaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkdyb3VwaW5nQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblByZWNoZWNrQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVsYXRpb25zaGlwVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25SZXF1aXJlZEJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblNlbGVjdGlvbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5RGVmaW5pdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aXZpdHlQYXJ0aWNpcGFudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1VzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZG1pbmlzdHJhdGl2ZUdlbmRlcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZHZlcnNlRXZlbnRBY3R1YWxpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWdncmVnYXRpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXBwb2ludG1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBc3NlcnRpb25PcGVyYXRvclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRBZ2VudE5ldHdvcmtUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpbmRpbmdTdHJlbmd0aCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdENhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJ1bmRsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FwYWJpbGl0eVN0YXRlbWVudEtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eUtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhbkludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlVGVhbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsYWltU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsaW5pY2FsSW1wcmVzc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU2VhcmNoU3VwcG9ydCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU3lzdGVtQ29udGVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUhpZXJhcmNoeU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tbXVuaWNhdGlvblByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcGFydG1lbnRDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvbkF0dGVzdGF0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25jZXB0TWFwRXF1aXZhbGVuY2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEdyb3VwVW5tYXBwZWRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsRGVsZXRlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsUmVhZFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zZW50RGF0YU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFByb3Zpc2lvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnN0cmFpbnRTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250YWN0UG9pbnRTeXN0ZW0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0UHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJhY3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJpYnV0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvdmVyYWdlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEN1cnJlbmN5Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEYXlPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5c09mV2Vlayk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXRlY3RlZElzc3VlU2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDb2xvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VOYW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZVVzZVN0YXRlbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaWFnbm9zdGljUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERpc2NyaW1pbmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50Q29uZmlkZW50aWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlZmVyZW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbGlnaWJpbGl0eVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmFibGVXaGVuQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuZHBvaW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFcGlzb2RlT2ZDYXJlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50Q2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZlbnRUaW1pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZpZGVuY2VWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cG9zdXJlU3RhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXh0ZW5zaW9uQ29udGV4dFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkFsbFR5cGVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJEZWZpbmVkVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGV2aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlN1YnN0YW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSVmVyc2lvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGYW1pbHlIaXN0b3J5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZpbHRlck9wZXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZsYWdTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR29hbExpZmVjeWNsZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50UnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdyb3VwTWVhc3VyZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGFuY2VSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhZ2VHZW5lcmF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRlUGFyYW1ldGVyQ29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBIVFRQVmVyYik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGlmaWVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElkZW50aXR5QXNzdXJhbmNlTGV2ZWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1hZ2luZ1N0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1tdW5pemF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VQcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW52b2ljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElzc3VlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIExvY2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lYXN1cmVSZXBvcnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGlhU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uRGlzcGVuc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1pbWVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWVVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYXJyYXRpdmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTm90ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTnV0cml0aWlvbk9yZGVySW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlvbk9yZGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uRGF0YVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT2JzZXJ2YXRpb25SYW5nZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT3BlcmF0aW9uUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9yaWVudGF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJhbWV0ZXJVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRSZXF1aXJlZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGFudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnROb3RpY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb2NlZHVyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVJlcHJlc2VudGF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb3BlcnR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm92ZW5hbmNlRW50aXR5Um9sZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQdWJsaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFsaXR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFudGl0eUNvbXBhcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1PcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWVzdGlvbm5haXJlSXRlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZUhhbmRsaW5nUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZVZlcnNpb25SdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcnJlZERvY3VtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbGF0ZWRBcnRpZmFjdFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVtaXR0YW5jZU91dGNvbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVwb3NpdG9yeVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaEVsZW1lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3R1ZHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hTdWJqZWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc291cmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVZlcnNpb25Qb2xpY3kpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzcG9uc2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3RmdWxDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSaXNrQXNzZXNzbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTUERYTGljZW5zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaEVudHJ5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hNb2RpZmllckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoUGFyYW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlY3Rpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcXVlbmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcnZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNsaWNpbmdSdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbG90U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNvcnREaXJlY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3BlY2ltZW5Db250YWluZWRQcmVmZXJlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJhbmRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcENvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcEdyb3VwVHlwZU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwSW5wdXRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcE1vZGVsTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBTb3VyY2VMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uQ2hhbm5lbFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseURlbGl2ZXJ5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGFza1ByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydEFjdGlvblJlc3VsdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRSZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0U2NyaXB0UmVxdWVzdE1ldGhvZENvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHJpZ2dlclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHlwZURlcml2YXRpb25SdWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVSZXN0ZnVsSW50ZXJhY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVURJRW50cnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVuaXRzT2ZUaW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uQmFzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25FeWVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBYUGF0aFVzYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBiYXNlNjRCaW5hcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgaWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9Cb29sZWFuKHZhbHVlIGJvb2xlYW4pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlKHZhbHVlIGRhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBkYXRlVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RlY2ltYWwodmFsdWUgZGVjaW1hbCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RhdGVUaW1lKHZhbHVlIGluc3RhbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlZ2VyKHZhbHVlIGludGVnZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgc3RyaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvVGltZSh2YWx1ZSB0aW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHVyaSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB4aHRtbCk6IHZhbHVlLnZhbHVlCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iRFFNRkhJUkhlbHBlcnMiIHN5c3RlbT0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iIHZlcnNpb249IjQuMC4xIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzoxLTM6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTk6NTgiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY6NS05OjU4IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjY6OC02OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjo4LTY6MTMiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc6OS03OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTo5LTk6NTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5OjE4LTk6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk6MTgtOTozMSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOToxOC05OjIzIiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTo0MC05OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NTEiIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NDUiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InBlcmlvZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OjM1LTU6NDUiIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTE6MS0xNTo4MiIgbmFtZT0iVG9RdWFudGl0eSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI6NS0xNTo4MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMjo4LTEyOjIzIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTI6OC0xMjoxNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzOjktMTM6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTU6OS0xNTo4MiIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTozNC0xNTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6MzQtMTU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1OjM0LTE1OjQxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTo2Mi0xNTo4MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NzQiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NjkiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTE6MzctMTE6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTIxOjEwNyIgbmFtZT0iVG9SYXRpbyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6NS0yMToxMDciIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTg6OC0xODoyMCIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjgtMTg6MTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OlJhdGlvIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxOTo5LTE5OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjIxOjktMjE6MTA3IiBjbGFzc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9Im51bWVyYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTozNS0yMTo2MSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE6NDYtMjE6NjAiIHBhdGg9Im51bWVyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTo0Ni0yMTo1MCIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkZW5vbWluYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTo3Ny0yMToxMDUiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjg4LTIxOjEwNCIgcGF0aD0iZGVub21pbmF0b3IiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6ODgtMjE6OTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhdGlvIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3OjMxLTE3OjQwIiBuYW1lPSJmaGlyOlJhdGlvIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM6MS0yNzo2MyIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ6NS0yNzo2MyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIyNDo4LTI0OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ6OC0yNDoxMiIgbmFtZT0icmFuZ2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTo5LTI1OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjc6OS0yNzo2MyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjI3OjE4LTI3OjM4IiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoyOS0yNzozNyIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjI5LTI3OjMzIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjc6NDEtMjc6NjIiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjUyLTI3OjYxIiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjUyLTI3OjU2IiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyYW5nZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzozNC0yMzo0MyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzg6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDo1LTM4OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMzA6OC0zMDoyMSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwOjgtMzA6MTMiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTo5LTMxOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjMzOjktMzg6OSIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMzQ6MTctMzQ6MzMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjIyIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIzNToxOS0zNTozNyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU6MTktMzU6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNToxOS0zNToyNCIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2ZXJzaW9uIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM2OjIwLTM2OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkaXNwbGF5Ij4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM3OjIwLTM3OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29kaW5nIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5OjMxLTI5OjQxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDc6OSIgbmFtZT0iVG9Db25jZXB0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MTo1LTQ3OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDE6OC00MToyMiIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjgtNDE6MTQiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29uY2VwdCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6OS00MjoxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI0NDo5LTQ3OjkiIGNsYXNzVHlwZT0idDpDb25jZXB0IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJjb2RlcyI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NToyMC00NTo1MiIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToyMC00NTozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NToyMC00NTozMyIgcGF0aD0iY29kaW5nIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iNDU6MzctNDU6NTIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NTo0NC00NTo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo1MSIgbmFtZT0iQyIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iZGlzcGxheSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NjoyMi00NjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MzMiIHBhdGg9InRleHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MjgiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwOjM1LTQwOjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUwOjEtNTA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1MDo0OC01MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTA6NDgtNTA6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUwOjMyLTUwOjQ0IiBuYW1lPSJmaGlyOkFjY291bnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTE6NjAtNTE6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjYwLTUxOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1MTozMi01MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjUyOjU0LTUyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo1NC01Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTI6MzItNTI6NTAiIG5hbWU9ImZoaXI6QWN0aW9uQ29uZGl0aW9uS2luZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUzOjEtNTM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Mzo1Ny01Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6NTctNTM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUzOjMyLTUzOjUzIiBuYW1lPSJmaGlyOkFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NDoxLTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTQ6NTYtNTQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjU2LTU0OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NDozMi01NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NToxLTU1OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTU6NTctNTU6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU1OjU3LTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NTozMi01NTo1MyIgbmFtZT0iZmhpcjpBY3Rpb25QcmVjaGVja0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTY6MS01Njo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjU2OjU3LTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Njo1Ny01Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTY6MzItNTY6NTMiIG5hbWU9ImZoaXI6QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Nzo1Ny01Nzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTc6NTctNTc6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU3OjMyLTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1ODoxLTU4OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTg6NTgtNTg6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU4OjU4LTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1ODozMi01ODo1NCIgbmFtZT0iZmhpcjpBY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtNTk6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1OTo1Ny01OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTk6NTctNTk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU5OjMyLTU5OjUzIiBuYW1lPSJmaGlyOkFjdGl2aXR5RGVmaW5pdGlvbktpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MDoxLTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjA6NTgtNjA6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYwOjU4LTYwOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2MDozMi02MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYxOjEtNjE6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2MTo0Ni02MTo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjE6NDYtNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYxOjMyLTYxOjQyIiBuYW1lPSJmaGlyOkFkZHJlc3NUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjI6MS02Mjo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYyOjQ1LTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Mjo0NS02Mjo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjI6MzItNjI6NDEiIG5hbWU9ImZoaXI6QWRkcmVzc1VzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYzOjEtNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2Mzo1NS02Mzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjM6NTUtNjM6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYzOjMyLTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY0OjU2LTY0OjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NDo1Ni02NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjQ6MzItNjQ6NTIiIG5hbWU9ImZoaXI6QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjU6MS02NTo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY1OjUwLTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NTo1MC02NTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjU6MzItNjU6NDYiIG5hbWU9ImZoaXI6QWdncmVnYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjY6MS02Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY2OjYxLTY2OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Njo2MS02Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjY6MzItNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NzoxLTY3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjc6NjQtNjc6NzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjY3OjY0LTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2NzozMi02Nzo2MCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2ODo2MS02ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjg6NjEtNjg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjY4OjMyLTY4OjU3IiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjk6MS02OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY5OjU3LTY5OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2OTo1Ny02OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjk6MzItNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcwOjEtNzA6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3MDo1Mi03MDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzA6NTItNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcwOjMyLTcwOjQ4IiBuYW1lPSJmaGlyOkFwcG9pbnRtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzE6MS03MTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjcxOjU3LTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3MTo1Ny03MTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzE6MzItNzE6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcyOjEtNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mjo1Ni03Mjo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzI6NTYtNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcyOjMyLTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjczOjEtNzM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mzo1Ny03Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzM6NTctNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjczOjMyLTczOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvblJlc3BvbnNlVHlwZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NDoxLTc0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzQ6NTEtNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc0OjUxLTc0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NDozMi03NDo0NyIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50QWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzU6MS03NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijc1OjYxLTc1OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3NTo2MS03NTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzU6MzItNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NjoxLTc2OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzY6NTItNzY6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc2OjUyLTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NjozMi03Njo0OCIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50T3V0Y29tZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc3OjEtNzc6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Nzo1MC03Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzc6NTAtNzc6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc3OjMyLTc3OjQ2IiBuYW1lPSJmaGlyOkJpbmRpbmdTdHJlbmd0aCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc4OjEtNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3ODo2OS03ODo3OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzg6NjktNzg6NzMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc4OjMyLTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTc5Ojc3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzk6NjctNzk6NzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc5OjY3LTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3OTozMi03OTo2MyIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgwOjEtODA6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDo3My04MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODA6NzMtODA6NzciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgwOjMyLTgwOjY5IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODE6MS04MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgxOjQ1LTgxOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4MTo0NS04MTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODE6MzItODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgyOjEtODI6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Mjo1OC04Mjo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODI6NTgtODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjMyLTgyOjU0IiBuYW1lPSJmaGlyOkNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODM6MS04Mzo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgzOjU1LTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4Mzo1NS04Mzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODM6MzItODM6NTEiIG5hbWU9ImZoaXI6Q2FyZVBsYW5BY3Rpdml0eUtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NDoxLTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODQ6NTctODQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg0OjU3LTg0OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NDozMi04NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODU6MS04NTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg1OjQ5LTg1OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4NTo0OS04NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODU6MzItODU6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5JbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NjoxLTg2OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODY6NDktODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg2OjQ5LTg2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NjozMi04Njo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg3OjEtODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo0OS04Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODc6NDktODc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg3OjMyLTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODg6MS04ODo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg4OjU5LTg4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1OS04ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6MzItODg6NTUiIG5hbWU9ImZoaXI6Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODk6MS04OTo4MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg5OjczLTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OTo3My04OTo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6MzItODk6NjkiIG5hbWU9ImZoaXI6Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MDoxLTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTA6NTEtOTA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjUxLTkwOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MDozMi05MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTE6MS05MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkxOjU0LTkxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1NC05MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6MzItOTE6NTAiIG5hbWU9ImZoaXI6Q2xhaW1SZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjkyOjEtOTI6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mjo0Ni05Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NDYtOTI6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjMyLTkyOjQyIiBuYW1lPSJmaGlyOkNsYWltU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTM6MS05Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkzOjU5LTkzOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mzo1OS05Mzo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTM6MzItOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTQ6MS05NDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk0OjUyLTk0OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5NDo1Mi05NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTQ6MzItOTQ6NDgiIG5hbWU9ImZoaXI6Q29kZVNlYXJjaFN1cHBvcnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NToxLTk1OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTU6NTYtOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk1OjU2LTk1OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NTozMi05NTo1MiIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtQ29udGVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NjoxLTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTY6NjEtOTY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjYxLTk2OjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjozMi05Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk3OjEtOTc6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Nzo1Ni05Nzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTc6NTYtOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk3OjMyLTk3OjUyIiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25Qcmlvcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk4OjEtOTg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5ODo2MS05ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTg6NjEtOTg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk4OjMyLTk4OjU3IiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTk6MS05OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk5OjU0LTk5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OTo1NC05OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTk6MzItOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwMDoxLTEwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwMDo1MC0xMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMDo1MC0xMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwMDozMi0xMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAxOjEtMTAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAxOjUwLTEwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAxOjUwLTEwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAxOjMyLTEwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDI6MS0xMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDI6NjEtMTAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDI6NjEtMTAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDI6MzItMTAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAzOjEtMTAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAzOjUyLTEwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAzOjUyLTEwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAzOjMyLTEwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNDoxLTEwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNDozMi0xMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA1OjEtMTA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA1OjYyLTEwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjYyLTEwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA1OjMyLTEwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDY6MS0xMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDY6NTgtMTA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6NTgtMTA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDY6MzItMTA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA3OjEtMTA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA3OjU2LTEwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA3OjU2LTEwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA3OjMyLTEwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDg6MS0xMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDg6NTMtMTA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDg6NTMtMTA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDg6MzItMTA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOToxLTEwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwOTo1NS0xMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwOTo1NS0xMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwOTozMi0xMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTA6MS0xMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTA6NDctMTEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTA6NDctMTEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTA6MzItMTEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExMToxLTExMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExMTo1My0xMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExMTo1My0xMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExMTozMi0xMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTEyOjEtMTEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEyOjUzLTExMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTEyOjUzLTExMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTEyOjMyLTExMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTM6MS0xMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTM6NTAtMTEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTM6NTAtMTEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTM6MzItMTEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNDoxLTExNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNDo2MC0xMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNDo2MC0xMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNDozMi0xMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNToxLTExNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTo0OS0xMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNTo0OS0xMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNTozMi0xMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTY6MS0xMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTY6NTAtMTE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTY6NTAtMTE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTY6MzItMTE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNzoxLTExNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNzo0OS0xMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0OS0xMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNzozMi0xMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTg6MS0xMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDctMTE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTg6NDctMTE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzItMTE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOToxLTExOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExOTo0NC0xMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExOTo0NC0xMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExOTozMi0xMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIwOjEtMTIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIwOjQ1LTEyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIwOjQ1LTEyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIwOjMyLTEyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIxOjEtMTIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIxOjU2LTEyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIxOjU2LTEyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIxOjMyLTEyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjI6MS0xMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjI6NTQtMTIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjI6NTQtMTIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjI6MzItMTIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjM6MS0xMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjM6NjMtMTIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjM6NjMtMTIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjM6MzItMTIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjQ6MS0xMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjQ6NjItMTI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjQ6NjItMTI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzItMTI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyNTo1NS0xMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyNTo1NS0xMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyNTozMi0xMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjY6MS0xMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjY6NTItMTI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjY6NTItMTI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjY6MzItMTI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI3OjEtMTI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI3OjY0LTEyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTI3OjY0LTEyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjMyLTEyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyODoxLTEyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyODo0OS0xMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyODo0OS0xMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozMi0xMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjk6NTQtMTI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjk6NTQtMTI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzItMTI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzA6NTktMTMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzA6NTktMTMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzItMTMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMToxLTEzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMTozMi0xMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMjoxLTEzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMjozMi0xMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzM6MS0xMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzM6NTgtMTMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzM6NTgtMTMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzM6MzItMTMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM0OjQ3LTEzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM0OjQ3LTEzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjMyLTEzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzU6MS0xMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzU6NTgtMTM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzU6NTgtMTM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6MzItMTM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM2OjEtMTM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM2OjU5LTEzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM2OjU5LTEzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM2OjMyLTEzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzc6MS0xMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzc6NjAtMTM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzc6NjAtMTM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6MzItMTM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzg6MS0xMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzg6NTktMTM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6NTktMTM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzg6MzItMTM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzOTo2MS0xMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzOTo2MS0xMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTozMi0xMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDA6MS0xNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDA6NjAtMTQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6NjAtMTQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzItMTQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDE6MS0xNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDE6NTMtMTQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6NTMtMTQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDE6MzItMTQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MjoxLTE0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MjozMi0xNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDM6MS0xNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDM6NTAtMTQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDM6NTAtMTQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6MzItMTQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NDozMi0xNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDU6MS0xNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDU6NTgtMTQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6NTgtMTQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzItMTQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ2OjEtMTQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ2OjU5LTE0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ2OjU5LTE0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ2OjMyLTE0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDc6MS0xNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDc6NTQtMTQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDc6NTQtMTQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6MzItMTQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDg6MS0xNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDg6NTQtMTQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6NTQtMTQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDg6MzItMTQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDk6MS0xNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDk6NDYtMTQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6NDYtMTQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzItMTQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjU1LTE1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjU1LTE1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MToxLTE1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTozMi0xNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjYxLTE1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjYxLTE1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MzoxLTE1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MzozMi0xNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NDoxLTE1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NDozMi0xNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NDctMTU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NDctMTU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NjoxLTE1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Njo1MC0xNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Njo1MC0xNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NjozMi0xNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU3OjEtMTU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU3OjUxLTE1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU3OjUxLTE1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU3OjMyLTE1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU4OjEtMTU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU4OjUxLTE1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU4OjUxLTE1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU4OjMyLTE1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU0LTE1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU0LTE1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjQ2LTE2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjQ2LTE2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo1NC0xNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo1NC0xNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MjoxLTE2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MjozMi0xNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NDUtMTYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NDUtMTYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjQ6MS0xNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjQ6NTQtMTY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjQ6NTQtMTY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjQ6MzItMTY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTUtMTY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTUtMTY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY2OjEtMTY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY2OjU0LTE2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY2OjU0LTE2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY2OjMyLTE2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY3OjEtMTY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY3OjMyLTE2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjg6MS0xNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjg6NDQtMTY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjg6NDQtMTY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjg6MzItMTY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1NC0xNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1NC0xNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MToxLTE3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MTo1My0xNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MTo1My0xNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MTozMi0xNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTcyOjEtMTcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTcyOjQzLTE3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTcyOjQzLTE3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTcyOjMyLTE3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo1My0xNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo1My0xNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc2OjEtMTc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc2OjYzLTE3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc2OjYzLTE3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc2OjMyLTE3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUzLTE3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUzLTE3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjAtMTc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjAtMTc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NDgtMTc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NDgtMTc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODA6MS0xODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODA6NDgtMTgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODA6NDgtMTgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODA6MzItMTgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODE6MS0xODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6NDQtMTgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODE6NDQtMTgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODE6MzItMTgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo0My0xODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo0My0xODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NDYtMTgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NDYtMTgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjQzLTE4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjQzLTE4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NToxLTE4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4NTo0NS0xODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4NTo0NS0xODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NTozMi0xODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg3OjEtMTg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg3OjMyLTE4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4ODoxLTE4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4ODo1NC0xODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4ODo1NC0xODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4ODozMi0xODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OToxLTE4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTo1Mi0xODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OTo1Mi0xODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4OTozMi0xODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTA6MS0xOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTA6NDYtMTkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTA6NDYtMTkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTA6MzItMTkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjY1LTE5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjY1LTE5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTI6MS0xOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTI6NTktMTkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTI6NTktMTkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTI6MzItMTkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NDoxLTE5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NDozMi0xOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTU6MS0xOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTU6NjAtMTk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTU6NjAtMTk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTU6MzItMTk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTY6MS0xOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTY6NTgtMTk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTY6NTgtMTk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTY6MzItMTk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk3OjEtMTk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjYwLTE5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk3OjYwLTE5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk3OjMyLTE5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjUxLTE5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjUxLTE5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk5OjEtMTk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk5OjYyLTE5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk5OjYyLTE5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk5OjMyLTE5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDA6MS0yMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDA6NjUtMjAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDA6NjUtMjAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDA6MzItMjAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMToxLTIwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMTo0My0yMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMTo0My0yMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMTozMi0yMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NDItMjAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NDItMjAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDM6MS0yMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDM6NjEtMjAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDM6NjEtMjAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDM6MzItMjAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA0OjEtMjA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA0OjUxLTIwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA0OjUxLTIwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA0OjMyLTIwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjUwLTIwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjUwLTIwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NDMtMjA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NDMtMjA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTUtMjA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTUtMjA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA5OjEtMjA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA5OjU0LTIwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA5OjU0LTIwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA5OjMyLTIwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEwOjEtMjEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEwOjU5LTIxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEwOjU5LTIxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEwOjMyLTIxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTE6MS0yMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTE6NTItMjExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTE6NTItMjExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTE6MzItMjExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjQ4LTIxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjQ4LTIxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEzOjEtMjEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEzOjU2LTIxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEzOjU2LTIxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEzOjMyLTIxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTQ6MS0yMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTQ6NTAtMjE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTQ6NTAtMjE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTQ6MzItMjE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE2OjEtMjE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjU0LTIxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE2OjU0LTIxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE2OjMyLTIxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE3OjEtMjE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE3OjUyLTIxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE3OjUyLTIxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE3OjMyLTIxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxODoxLTIxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODo1NC0yMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxODo1NC0yMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxODozMi0yMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo1NC0yMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo1NC0yMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyMDoxLTIyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyMDozMi0yMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjUwLTIyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjUwLTIyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTctMjIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTctMjIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NDctMjIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NDctMjIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNDoxLTIyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNDozMi0yMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjU6NTItMjI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjU6NTItMjI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6MzItMjI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI2OjEtMjI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI2OjQ2LTIyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI2OjQ2LTIyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI2OjMyLTIyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNzoxLTIyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNzo1My0yMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNzo1My0yMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNzozMi0yMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI4OjEtMjI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI4OjYwLTIyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI4OjYwLTIyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI4OjMyLTIyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI5OjEtMjI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI5OjU2LTIyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI5OjU2LTIyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI5OjMyLTIyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NjItMjMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NjItMjMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzI6MS0yMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzI6NTYtMjMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzI6NTYtMjMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzI6MzItMjMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzozMi0yMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNDoxLTIzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNDozMi0yMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNToxLTIzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNTozMi0yMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzY6MS0yMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6NDktMjM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzY6NDktMjM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzY6MzItMjM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM3OjEtMjM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM3OjQ4LTIzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM3OjQ4LTIzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM3OjMyLTIzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM4OjEtMjM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM4OjUwLTIzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM4OjUwLTIzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM4OjMyLTIzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzk6MS0yMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzk6NDgtMjM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzk6NDgtMjM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzk6MzItMjM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NTQtMjQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NTQtMjQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTQtMjQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTQtMjQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDI6MS0yNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDI6NTYtMjQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDI6NTYtMjQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDI6MzItMjQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MzoxLTI0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MzozMi0yNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ0OjEtMjQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ0OjU2LTI0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ0OjU2LTI0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ0OjMyLTI0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NDctMjQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NDctMjQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NjoxLTI0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NjozMi0yNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ3OjEtMjQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ3OjU1LTI0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ3OjU1LTI0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ3OjMyLTI0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0ODoxLTI0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0ODozMi0yNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NTEtMjQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NTEtMjQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTA6MS0yNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTA6NTAtMjUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTA6NTAtMjUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTA6MzItMjUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1My0yNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1My0yNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjUwLTI1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjUwLTI1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTM6MS0yNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTM6NDYtMjUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTM6NDYtMjUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTM6MzItMjUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU0OjEtMjU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU0OjQ3LTI1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU0OjQ3LTI1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU0OjMyLTI1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NTUtMjU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NTUtMjU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU2OjEtMjU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU2OjU3LTI1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU2OjU3LTI1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU2OjMyLTI1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjU1LTI1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjU1LTI1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1ODoxLTI1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1ODozMi0yNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjQ1LTI1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjQ1LTI1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ4LTI2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ4LTI2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYxOjEtMjYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYxOjYyLTI2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYxOjYyLTI2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjMyLTI2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjI6MS0yNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjI6NDktMjYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjI6NDktMjYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjI6MzItMjYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYzOjEtMjYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYzOjQxLTI2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYzOjQxLTI2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYzOjMyLTI2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NDUtMjY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NDUtMjY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTgtMjY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTgtMjY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU4LTI2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU4LTI2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2NzoxLTI2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2NzozMi0yNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2ODoxLTI2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2ODozMi0yNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY5OjU2LTI2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY5OjU2LTI2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY5OjMyLTI2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzA6MS0yNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzA6NjEtMjcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6NjEtMjcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzA6MzItMjcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcxOjEtMjcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcxOjYxLTI3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcxOjYxLTI3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcxOjMyLTI3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MjoxLTI3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MjozMi0yNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjczOjEtMjczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjczOjU4LTI3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjczOjU4LTI3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjczOjMyLTI3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1My0yNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1My0yNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc1OjEtMjc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc1OjU1LTI3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc1OjU1LTI3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc1OjMyLTI3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NjoxLTI3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Njo1NC0yNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Njo1NC0yNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NjozMi0yNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NzoxLTI3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NzozMi0yNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc4OjEtMjc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc4OjQ1LTI3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc4OjQ1LTI3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc4OjMyLTI3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc5OjEtMjc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc5OjQ3LTI3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc5OjQ3LTI3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc5OjMyLTI3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDUtMjgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDUtMjgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NTctMjgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NTctMjgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODI6MS0yODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODI6NjAtMjgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODI6NjAtMjgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODI6MzItMjgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NTEtMjgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NTEtMjgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODQ6MS0yODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODQ6NTEtMjg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODQ6NTEtMjg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODQ6MzItMjg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODU6MS0yODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODU6NjItMjg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODU6NjItMjg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODU6MzItMjg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODc6MS0yODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODc6NTMtMjg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODc6NTMtMjg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6MzItMjg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkwOjEtMjkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkwOjQ2LTI5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkwOjQ2LTI5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkwOjMyLTI5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MToxLTI5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MTozOC0yOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5MTozOC0yOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MTozMi0yOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkyOjEtMjkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkyOjMyLTI5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTM6MS0yOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTM6NDUtMjkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTM6NDUtMjkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTM6MzItMjkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTQ6MS0yOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTQ6NDUtMjk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTQ6NDUtMjk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTQ6MzItMjk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NDctMjk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NDctMjk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NjoxLTI5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NjozMi0yOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTc6MS0yOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTc6NDctMjk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTc6NDctMjk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTc6MzItMjk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5ODoxLTI5ODo0NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5ODozNy0yOTg6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5ODozNy0yOTg6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5ODozMi0yOTg6MzMiIG5hbWU9ImZoaXI6aWQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTk6MS0yOTk6NTMiIG5hbWU9IlRvQm9vbGVhbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjQzLTI5OTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjQzLTI5OTo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMzLTI5OTozOSIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzAwOjEtMzAwOjQ3IiBuYW1lPSJUb0RhdGUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMDozNy0zMDA6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMDozNy0zMDA6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMDozMC0zMDA6MzMiIG5hbWU9ImZoaXI6ZGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1NSIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzAxOjQ1LTMwMTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzAxOjQ1LTMwMTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzAxOjM0LTMwMTo0MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMjoxLTMwMjo1MyIgbmFtZT0iVG9EZWNpbWFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDMtMzAyOjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDMtMzAyOjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzMtMzAyOjM5IiBuYW1lPSJmaGlyOmRlY2ltYWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NTQiIG5hbWU9IlRvRGF0ZVRpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMzo0NC0zMDM6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMzo0NC0zMDM6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMzozNC0zMDM6NDAiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNDoxLTMwNDo1MyIgbmFtZT0iVG9JbnRlZ2VyIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDQ6NDMtMzA0OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDQ6NDMtMzA0OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDQ6MzMtMzA0OjM5IiBuYW1lPSJmaGlyOmludGVnZXIiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDU6MS0zMDU6NTEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDU6NDEtMzA1OjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDU6NDEtMzA1OjQ1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6MzItMzA1OjM3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNjoxLTMwNjo0NyIgbmFtZT0iVG9UaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6MzctMzA2OjQ3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6MzctMzA2OjQxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzAtMzA2OjMzIiBuYW1lPSJmaGlyOnRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDc6MS0zMDc6NDgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDc6MzgtMzA3OjQ4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDc6MzgtMzA3OjQyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDc6MzItMzA3OjM0IiBuYW1lPSJmaGlyOnVyaSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwODoxLTMwODo1MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODo0MC0zMDg6NTAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwODo0MC0zMDg6NDQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwODozMi0zMDg6MzYiIG5hbWU9ImZoaXI6eGh0bWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgPC9zdGF0ZW1lbnRzPgo8L2xpYnJhcnk+Cg==" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiRFFNRkhJUkhlbHBlcnMiLAogICAgICAgICAic3lzdGVtIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbSIsCiAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgfSwKICAgICAgInNjaGVtYUlkZW50aWZpZXIiIDogewogICAgICAgICAiaWQiIDogInVybjpobDctb3JnOmVsbSIsCiAgICAgICAgICJ2ZXJzaW9uIiA6ICJyMSIKICAgICAgfSwKICAgICAgInVzaW5ncyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIlN5c3RlbSIsCiAgICAgICAgICAgICJ1cmkiIDogInVybjpobDctb3JnOmVsbS10eXBlczpyMSIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzoxLTM6MjYiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSIiwKICAgICAgICAgICAgInVyaSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpciIsCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MS05OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjo1LTk6NTgiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6OC02OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6OC02OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzo5LTc6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo5LTk6NTgiLAogICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOToxOC05OjM3IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OjE4LTk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk6MTgtOToyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk6NDAtOTo1NyIsCiAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo0MC05OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo0MC05OjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MzUtNTo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI6NS0xNTo4MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI6OC0xMjoyMyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjo4LTEyOjE1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6OS0xMzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjktMTU6ODIiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTozNC0xNTo1MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6MzQtMTU6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjM0LTE1OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6NjItMTU6ODAiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjYyLTE1Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjYyLTE1OjY5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE6MzctMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTIxOjEwNyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1JhdGlvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjUtMjE6MTA3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo4LTE4OjIwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjgtMTg6MTIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTo5LTE5OjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6OS0yMToxMDciLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MzUtMjE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6NDYtMjE6NjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibnVtZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTo0Ni0yMTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlbm9taW5hdG9yIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjc3LTIxOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTo4OC0yMToxMDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjg4LTIxOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzozMS0xNzo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEtMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDo1LTI3OjYzIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDo4LTI0OjIwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjgtMjQ6MTIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjktMjU6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6OS0yNzo2MyIsCiAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoxOC0yNzozOCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyOS0yNzozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjI5LTI3OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjQxLTI3OjYyIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjUyLTI3OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjUyLTI3OjU2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjM0LTIzOjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6MS0zODo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo1LTM4OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOjgtMzA6MjEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6OC0zMDoxMyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjktMzE6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzo5LTM4OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MTctMzQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjE3LTM0OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjE3LTM0OjIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTktMzU6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OjE5LTM1OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTktMzU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6MjAtMzY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjIwLTM2OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjIwLTM2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjIwLTM3OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyMC0zNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyMC0zNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6MzEtMjk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MS00Nzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo1LTQ3OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjgtNDE6MjIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6OC00MToxNCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mjo5LTQyOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDQ6OS00Nzo5IiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RlcyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyMC00NTo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyMC00NTozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjIwLTQ1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozNy00NTo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjQ0LTQ1OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6MjItNDY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIyLTQ2OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInRleHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIyLTQ2OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjM1LTQwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjEtNTA6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6NDgtNTA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjQ4LTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDozMi01MDo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY2NvdW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MS01MTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2MC01MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NjAtNTE6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjMyLTUxOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxLTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjU0LTUyOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo1NC01Mjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MzItNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjEtNTM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NTctNTM6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjU3LTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzozMi01Mzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MS01NDo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo1Ni01NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6NTYtNTQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjMyLTU0OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU1OjEtNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTU6NTctNTU6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU1OjU3LTU1OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NTozMi01NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MS01Njo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Njo1Ny01Njo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NTctNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMyLTU2OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlbGF0aW9uc2hpcFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NzoxLTU3OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjU3LTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Nzo1Ny01Nzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTc6MzItNTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVxdWlyZWRCZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU4OjEtNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTg6NTgtNTg6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU4OjU4LTU4OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1ODozMi01ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtNTk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTk6NTctNTk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjU3LTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozMi01OTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eURlZmluaXRpb25LaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6MS02MDo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1OC02MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6NTgtNjA6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMyLTYwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5UGFydGljaXBhbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MS02MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MTo0Ni02MTo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6NDYtNjE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjMyLTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6MS02Mjo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2Mjo0NS02Mjo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6NDUtNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjMyLTYyOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MzoxLTYzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYzOjU1LTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2Mzo1NS02Mzo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MzItNjM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRtaW5pc3RyYXRpdmVHZW5kZXIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDoxLTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjU2LTY0OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDo1Ni02NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MzItNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MS02NTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NTo1MC02NTo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY1OjMyLTY1OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZ3JlZ2F0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY2OjEtNjY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjY6NjEtNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY2OjYxLTY2OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NjozMi02Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjEtNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjc6NjQtNjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjY0LTY3OjY4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzozMi02Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjg6NjEtNjg6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjYxLTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2ODozMi02ODo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjEtNjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjk6NTctNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjU3LTY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2OTozMi02OTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MS03MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MDo1Mi03MDo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTItNzA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcwOjMyLTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6MS03MTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MTo1Ny03MTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6NTctNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcxOjMyLTcxOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbkRpcmVjdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxLTcyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjU2LTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mjo1Ni03Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzI6MzItNzI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uT3BlcmF0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MS03Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo1Ny03Mzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6NTctNzM6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjMyLTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NDoxLTc0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUxLTc0OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NDo1MS03NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzQ6MzItNzQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc1OjEtNzU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzU6NjEtNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc1OjYxLTc1OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NTozMi03NTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjEtNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzY6NTItNzY6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjUyLTc2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NjozMi03Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjEtNzc6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6NTAtNzc6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjUwLTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzozMi03Nzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW5kaW5nU3RyZW5ndGgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODoxLTc4Ojc5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjY5LTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo2OS03ODo3MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MzItNzg6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjEtNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6NjctNzk6NzciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjY3LTc5OjcxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTozMi03OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjEtODA6ODMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODA6NzMtODA6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjczLTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDozMi04MDo2OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjEtODE6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6NDUtODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjQ1LTgxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTozMi04MTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CdW5kbGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MS04Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo1OC04Mjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6NTgtODI6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjMyLTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MS04Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1NS04Mzo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NTUtODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjMyLTgzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVQbGFuQWN0aXZpdHlLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODQ6MS04NDo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDo1Ny04NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODQ6NTctODQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjMyLTg0OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVQbGFuQWN0aXZpdHlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NToxLTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg1OjQ5LTg1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NTo0OS04NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6MzItODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxLTg2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjQ5LTg2OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0OS04Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6MzItODY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzoxLTg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjQ5LTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo0OS04Nzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MzItODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVRlYW1TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxLTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjU5LTg4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1OS04ODo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzItODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MS04OTo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3My04OTo4MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NzMtODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjMyLTg5OjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6MS05MDo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo1MS05MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NTEtOTA6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjMyLTkwOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNoYXJnZUl0ZW1TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxLTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjU0LTkxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1NC05MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzItOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjEtOTI6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjQ2LTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjozMi05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DbGFpbVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjEtOTM6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NTktOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjU5LTkzOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzozMi05Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DbGluaWNhbEltcHJlc3Npb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDoxLTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjUyLTk0OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDo1Mi05NDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MzItOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxLTk1OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjU2LTk1OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTo1Ni05NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MzItOTU6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MS05Njo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo2MS05Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NjEtOTY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjMyLTk2OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6MS05Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Nzo1Ni05Nzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6NTYtOTc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk3OjMyLTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjEtOTg6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6NjEtOTg6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjYxLTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMi05ODo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjEtOTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTk6NTQtOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjU0LTk5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTozMi05OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21tdW5pY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEtMTAwOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo1MC0xMDA6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo1MC0xMDA6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDozMi0xMDA6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcGFydG1lbnRDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAxOjEtMTAxOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTo1MC0xMDE6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTo1MC0xMDE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTozMi0xMDE6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcGFydG1lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAyOjEtMTAyOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjo2MS0xMDI6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjo2MS0xMDI6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjozMi0xMDI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MS0xMDM6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjUyLTEwMzo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjUyLTEwMzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjMyLTEwMzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21wb3NpdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNDoxLTEwNDo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6NTYtMTA0OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6NTYtMTA0OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MzItMTA0OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmNlcHRNYXBFcXVpdmFsZW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToxLTEwNTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6NjItMTA1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6NjItMTA1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MzItMTA1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNjoxLTEwNjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6NTgtMTA2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6NTgtMTA2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MzItMTA2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjEtMTA3OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo1Ni0xMDc6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo1Ni0xMDc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzozMi0xMDc6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uYWxSZWFkU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjEtMTA4OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODo1My0xMDg6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODo1My0xMDg6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODozMi0xMDg6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudERhdGFNZWFuaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA5OjEtMTA5OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1NS0xMDk6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1NS0xMDk6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTozMi0xMDk6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFByb3Zpc2lvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6MS0xMTA6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjQ3LTExMDo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjQ3LTExMDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMyLTExMDo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25zZW50U3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MS0xMTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjUzLTExMTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjUzLTExMTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjMyLTExMTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25zdHJhaW50U2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6MS0xMTI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjUzLTExMjo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjUzLTExMjo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjMyLTExMjo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250YWN0UG9pbnRTeXN0ZW0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTM6MS0xMTM6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjUwLTExMzo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjUwLTExMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjMyLTExMzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250YWN0UG9pbnRVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6MS0xMTQ6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjYwLTExNDo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjYwLTExNDo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjMyLTExNDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250cmFjdFB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjEtMTE1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTo0OS0xMTU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTo0OS0xMTU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTozMi0xMTU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6MS0xMTY6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjUwLTExNjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjUwLTExNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjMyLTExNjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250cmlidXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MS0xMTc6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQ5LTExNzo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQ5LTExNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjMyLTExNzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db3ZlcmFnZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTExODo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDctMTE4OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDctMTE4OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzItMTE4OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUN1cnJlbmN5Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExOToxLTExOTo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6NDQtMTE5OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6NDQtMTE5OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6MzItMTE5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURheU9mV2VlayIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMDoxLTEyMDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6NDUtMTIwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6NDUtMTIwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6MzItMTIwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURheXNPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjE6MS0xMjE6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjU2LTEyMTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjU2LTEyMTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjMyLTEyMTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXRlY3RlZElzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjI6MS0xMjI6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjU0LTEyMjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjU0LTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXRlY3RlZElzc3VlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTIzOjEtMTIzOjczIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzo2My0xMjM6NzMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzo2My0xMjM6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzozMi0xMjM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDoxLTEyNDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6NjItMTI0OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6NjItMTI0OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6MzItMTI0OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNToxLTEyNTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6NTUtMTI1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6NTUtMTI1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MzItMTI1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI2OjEtMTI2OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjo1Mi0xMjY6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjo1Mi0xMjY6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozMi0xMjY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljQ29sb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjY0LTEyNzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjY0LTEyNzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjMyLTEyNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6NDktMTI4OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6NDktMTI4OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6MzItMTI4OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU5hbWVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI5OjEtMTI5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTo1NC0xMjk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTo1NC0xMjk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTozMi0xMjk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMDoxLTEzMDo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6NTktMTMwOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6NTktMTMwOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzItMTMwOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVVzZVN0YXRlbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMToxLTEzMTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6NTctMTMxOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6NTctMTMxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MzItMTMxOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURpYWdub3N0aWNSZXBvcnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzI6MS0xMzI6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjUyLTEzMjo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjUyLTEzMjo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjMyLTEzMjo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaXNjcmltaW5hdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMzoxLTEzMzo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6NTgtMTMzOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6NTgtMTMzOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzItMTMzOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50Q29uZmlkZW50aWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDo0Ny0xMzQ6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDo0Ny0xMzQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDozMi0xMzQ6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTo1OC0xMzU6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTo1OC0xMzU6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTozMi0xMzU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6MS0xMzY6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjU5LTEzNjo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjU5LTEzNjo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjMyLTEzNjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjMyLTEzNzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGlnaWJpbGl0eVJlcXVlc3RQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM4OjEtMTM4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo1OS0xMzg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo1OS0xMzg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODozMi0xMzg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjEtMTM5OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo2MS0xMzk6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo2MS0xMzk6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTozMi0xMzk6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6MS0xNDA6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjYwLTE0MDo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjYwLTE0MDo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjMyLTE0MDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGlnaWJpbGl0eVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo1My0xNDE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo1My0xNDE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTozMi0xNDE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5hYmxlV2hlbkJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo1OC0xNDI6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo1OC0xNDI6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MjozMi0xNDI6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6MS0xNDM6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjMyLTE0Mzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbmNvdW50ZXJTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MS0xNDQ6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjQ5LTE0NDo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjQ5LTE0NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjMyLTE0NDo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbmRwb2ludFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NToxLTE0NTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTgtMTQ1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTgtMTQ1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MzItMTQ1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ2OjEtMTQ2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjozMi0xNDY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5yb2xsbWVudFJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo1NC0xNDc6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo1NC0xNDc6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMi0xNDc6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXBpc29kZU9mQ2FyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxLTE0ODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NTQtMTQ4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NTQtMTQ4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MzItMTQ4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV2ZW50Q2FwYWJpbGl0eU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MS0xNDk6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjQ2LTE0OTo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjQ2LTE0OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjMyLTE0OTo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudFRpbWluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTUtMTUwOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTUtMTUwOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV2aWRlbmNlVmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjEtMTUxOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1OS0xNTE6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1OS0xNTE6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTozMi0xNTE6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo2MS0xNTI6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo2MS0xNTI6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwbGFuYXRpb25PZkJlbmVmaXRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MS0xNTM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjQ4LTE1Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjQ4LTE1Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjMyLTE1Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FeHBvc3VyZVN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjEtMTU0OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDo1NS0xNTQ6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDo1NS0xNTQ6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDozMi0xNTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXh0ZW5zaW9uQ29udGV4dFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjQ3LTE1NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjQ3LTE1NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSQWxsVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTY6MS0xNTY6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjUwLTE1Njo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjUwLTE1Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjMyLTE1Njo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSRGVmaW5lZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MS0xNTc6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjUxLTE1Nzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjUxLTE1Nzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjMyLTE1Nzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSRGV2aWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjEtMTU4OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODo1MS0xNTg6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODo1MS0xNTg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODozMi0xNTg6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlJlc291cmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTQtMTU5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTQtMTU5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJTdWJzdGFuY2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjQ2LTE2MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjQ2LTE2MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSVmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NTQtMTYxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NTQtMTYxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZhbWlseUhpc3RvcnlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MS0xNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjQ5LTE2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjQ5LTE2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjMyLTE2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GaWx0ZXJPcGVyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NDUtMTYzOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NDUtMTYzOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZsYWdTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjQ6MS0xNjQ6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjU0LTE2NDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjU0LTE2NDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjMyLTE2NDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Hb2FsTGlmZWN5Y2xlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1NS0xNjU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1NS0xNjU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFJ1bGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjY6MS0xNjY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjU0LTE2Njo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjU0LTE2Njo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjMyLTE2Njo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HcmFwaENvbXBhcnRtZW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjEtMTY3OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Nzo0Ny0xNjc6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Nzo0Ny0xNjc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NzozMi0xNjc6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JvdXBNZWFzdXJlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY4OjEtMTY4OjU0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODo0NC0xNjg6NTQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODo0NC0xNjg6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODozMi0xNjg6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JvdXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTQtMTcwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTQtMTcwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRlUGFnZUdlbmVyYXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzE6MS0xNzE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjUzLTE3MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjUzLTE3MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjMyLTE3MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhcmFtZXRlckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MS0xNzI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjQzLTE3Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjQzLTE3Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjMyLTE3MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1IVFRQVmVyYiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NDgtMTczOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NDgtMTczOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlkZW50aWZpZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjU3LTE3NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjU3LTE3NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGl0eUFzc3VyYW5jZUxldmVsIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo1My0xNzU6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo1My0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW1hZ2luZ1N0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjEtMTc2OjczIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3Njo2My0xNzY6NzMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3Njo2My0xNzY6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NjozMi0xNzY6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW1tdW5pemF0aW9uRXZhbHVhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTMtMTc3OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTMtMTc3OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjAtMTc4OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjAtMTc4OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjQ4LTE3OTo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjQ4LTE3OTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgwOjEtMTgwOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDo0OC0xODA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDo0OC0xODA6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDozMi0xODA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SXNzdWVTZXZlcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MToxLTE4MTo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6NDQtMTgxOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6NDQtMTgxOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6MzItMTgxOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NDMtMTgyOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NDMtMTgyOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmtUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo0Ni0xODM6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo0Ni0xODM6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlua2FnZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjQzLTE4NDo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjQzLTE4NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NToxLTE4NTo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6NDUtMTg1OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6NDUtMTg1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6MzItMTg1OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ3LTE4Njo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ3LTE4Njo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Mb2NhdGlvbk1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MS0xODc6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjQ5LTE4Nzo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjQ5LTE4Nzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjMyLTE4Nzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Mb2NhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODoxLTE4ODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NTQtMTg4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NTQtMTg4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6MzItMTg4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lYXN1cmVSZXBvcnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6MS0xODk6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjUyLTE4OTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjUyLTE4OTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjMyLTE4OTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MDoxLTE5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6NDYtMTkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6NDYtMTkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MzItMTkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGlhU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo2NS0xOTE6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo2NS0xOTE6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbkFkbWluaXN0cmF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkyOjEtMTkyOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mjo1OS0xOTI6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mjo1OS0xOTI6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjozMi0xOTI6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo2MC0xOTM6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo2MC0xOTM6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDoxLTE5NDo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6NTgtMTk0OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6NTgtMTk0OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6MzItMTk0OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjEtMTk1OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTo2MC0xOTU6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTo2MC0xOTU6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTozMi0xOTU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NjoxLTE5Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6NTgtMTk2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6NTgtMTk2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MzItMTk2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjEtMTk3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Nzo2MC0xOTc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Nzo2MC0xOTc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzozMi0xOTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvblN0YXRlbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NTEtMTk4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NTEtMTk4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTk6MS0xOTk6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjYyLTE5OTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjYyLTE5OTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjMyLTE5OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MS0yMDA6NzUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjY1LTIwMDo3NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjY1LTIwMDo2OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjMyLTIwMDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZXNzYWdlaGVhZGVyX1Jlc3BvbnNlX1JlcXVlc3QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MS0yMDE6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjQzLTIwMTo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjQzLTIwMTo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjMyLTIwMTozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NaW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo1MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NDItMjAyOjUyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NDItMjAyOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU5hbWVVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDM6MS0yMDM6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjYxLTIwMzo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjYxLTIwMzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjMyLTIwMzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1JZGVudGlmaWVyVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNDoxLTIwNDo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6NTEtMjA0OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6NTEtMjA0OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MzItMjA0OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU5hbWluZ1N5c3RlbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjUwLTIwNTo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjUwLTIwNTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYXJyYXRpdmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjQzLTIwNjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjQzLTIwNjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Ob3RlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU51dHJpdGlpb25PcmRlckludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTUtMjA4OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTUtMjA4OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU51dHJpdGlvbk9yZGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjA5OjEtMjA5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTo1NC0yMDk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTo1NC0yMDk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTozMi0yMDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9T2JzZXJ2YXRpb25EYXRhVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDoxLTIxMDo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6NTktMjEwOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6NTktMjEwOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6MzItMjEwOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMToxLTIxMTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6NTItMjExOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6NTItMjExOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6MzItMjExOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo0OC0yMTI6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo0OC0yMTI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9T3BlcmF0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzoxLTIxMzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6NTYtMjEzOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6NTYtMjEzOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6MzItMjEzOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvblBhcmFtZXRlclVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDoxLTIxNDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6NTAtMjE0OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6NTAtMjE0OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6MzItMjE0OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9yaWVudGF0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDctMjE1OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDctMjE1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcmFtZXRlclVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjoxLTIxNjo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6NTQtMjE2OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6NTQtMjE2OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6MzItMjE2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcnRpY2lwYW50UmVxdWlyZWQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTc6MS0yMTc6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjUyLTIxNzo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjUyLTIxNzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjMyLTIxNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODoxLTIxODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6NTQtMjE4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6NTQtMjE4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MzItMjE4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcnRpY2lwYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjU0LTIxOTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjU0LTIxOTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXltZW50Tm90aWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjEtMjIwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDo2Mi0yMjA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDo2Mi0yMjA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDozMi0yMjA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1MC0yMjE6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1MC0yMjE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvY2VkdXJlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1Ny0yMjI6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1Ny0yMjI6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvcGVydHlSZXByZXNlbnRhdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NDctMjIzOjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NDctMjIzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNDoxLTIyNDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6NTUtMjI0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6NTUtMjI0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MzItMjI0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3ZlbmFuY2VFbnRpdHlSb2xlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjEtMjI1OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1Mi0yMjU6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1Mi0yMjU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTozMi0yMjU6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHVibGljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MS0yMjY6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjQ2LTIyNjo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjQ2LTIyNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjMyLTIyNjo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFsaXR5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzoxLTIyNzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6NTMtMjI3OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6NTMtMjI3OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6MzItMjI3OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVF1YW50aXR5Q29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyODoxLTIyODo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6NjAtMjI4OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6NjAtMjI4OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MzItMjI4OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MS0yMjk6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjU2LTIyOTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjU2LTIyOTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjMyLTIyOTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjYyLTIzMDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjYyLTIzMDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU4LTIzMTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU4LTIzMTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VIYW5kbGluZ1BvbGljeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjoxLTIzMjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6NTYtMjMyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6NTYtMjMyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6MzItMjMyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlZmVyZW5jZVZlcnNpb25SdWxlcyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMzoxLTIzMzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NTctMjMzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NTctMjMzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MzItMjMzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlZmVycmVkRG9jdW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MS0yMzQ6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjU0LTIzNDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjU0LTIzNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjMyLTIzNDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWxhdGVkQXJ0aWZhY3RUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM1OjEtMjM1OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTo1Mi0yMzU6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTo1Mi0yMzU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTozMi0yMzU6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVtaXR0YW5jZU91dGNvbWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6MS0yMzY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjQ5LTIzNjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjQ5LTIzNjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjMyLTIzNjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXBvc2l0b3J5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzoxLTIzNzo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6NDgtMjM3OjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6NDgtMjM3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6MzItMjM3OjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlcXVlc3RJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MS0yMzg6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjUwLTIzODo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjUwLTIzODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjMyLTIzODo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MS0yMzk6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjQ4LTIzOTo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjQ4LTIzOTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjMyLTIzOTo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo1NC0yNDA6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo1NC0yNDA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hFbGVtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTQtMjQxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTQtMjQxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoU3R1ZHlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MS0yNDI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjU2LTI0Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjU2LTI0Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjMyLTI0Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN1YmplY3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6MS0yNDM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjQ3LTI0Mzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjQ3LTI0Mzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjMyLTI0Mzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDQ6MS0yNDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjU2LTI0NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjU2LTI0NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjMyLTI0NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZVZlcnNpb25Qb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjQ3LTI0NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjQ3LTI0NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNwb25zZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MS0yNDY6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjU2LTI0Njo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjU2LTI0Njo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjMyLTI0Njo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXN0ZnVsQ2FwYWJpbGl0eU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MS0yNDc6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjU1LTI0Nzo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjU1LTI0Nzo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjMyLTI0Nzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SaXNrQXNzZXNzbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0ODoxLTI0ODo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6NDYtMjQ4OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6NDYtMjQ4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MzItMjQ4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNQRFhMaWNlbnNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo1MS0yNDk6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo1MS0yNDk6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VhcmNoQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MDoxLTI1MDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6NTAtMjUwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6NTAtMjUwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MzItMjUwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaEVudHJ5TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTMtMjUxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTMtMjUxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaE1vZGlmaWVyQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NTAtMjUyOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NTAtMjUyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaFBhcmFtVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzoxLTI1Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6NDYtMjUzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6NDYtMjUzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6MzItMjUzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlY3Rpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU0OjEtMjU0OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDo0Ny0yNTQ6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDo0Ny0yNTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDozMi0yNTQ6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VxdWVuY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo1NS0yNTU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo1NS0yNTU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTY6MS0yNTY6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjU3LTI1Njo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjU3LTI1Njo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjMyLTI1Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1NS0yNTc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1NS0yNTc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MS0yNTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjQ3LTI1ODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjQ3LTI1ODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjMyLTI1ODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TbGljaW5nUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjQ1LTI1OTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjQ1LTI1OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TbG90U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0OC0yNjA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0OC0yNjA6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U29ydERpcmVjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MToxLTI2MTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6NjItMjYxOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6NjItMjYxOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MzItMjYxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjoxLTI2Mjo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6MzItMjYyOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNwZWNpbWVuU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjEtMjYzOjUxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mzo0MS0yNjM6NTEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mzo0MS0yNjM6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MzozMi0yNjM6MzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo0NS0yNjQ6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo0NS0yNjQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RyYW5kVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTgtMjY1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTgtMjY1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZURlZmluaXRpb25LaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1OC0yNjY6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1OC0yNjY6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwQ29udGV4dFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MS0yNjc6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjYwLTI2Nzo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjYwLTI2Nzo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjMyLTI2Nzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY4OjEtMjY4OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODo1Ni0yNjg6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODo1Ni0yNjg6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODozMi0yNjg6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwSW5wdXRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY5OjEtMjY5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo1Ni0yNjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo1Ni0yNjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTozMi0yNjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwTW9kZWxNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjEtMjcwOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDo2MS0yNzA6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDo2MS0yNzA6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDozMi0yNzA6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzE6MS0yNzE6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjYxLTI3MTo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjYxLTI3MTo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjMyLTI3MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjoxLTI3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6NTYtMjcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6NTYtMjcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6MzItMjcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFRyYW5zZm9ybSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MzoxLTI3Mzo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6NTgtMjczOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6NTgtMjczOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MzItMjczOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1My0yNzQ6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1My0yNzQ6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3Vic2NyaXB0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc1OjEtMjc1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTo1NS0yNzU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTo1NS0yNzU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTozMi0yNzU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5RGVsaXZlcnlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MS0yNzY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjU0LTI3Njo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjU0LTI3Njo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjMyLTI3Njo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdXBwbHlSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjEtMjc3OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Nzo1OS0yNzc6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Nzo1OS0yNzc6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NzozMi0yNzc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjEtMjc4OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODo0NS0yNzg6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODo0NS0yNzg6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODozMi0yNzg6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGFza0ludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OToxLTI3OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6NDctMjc5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6NDctMjc5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6MzItMjc5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDUtMjgwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDUtMjgwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjU3LTI4MTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjU3LTI4MTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0QWN0aW9uUmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjEtMjgyOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Mjo2MC0yODI6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Mjo2MC0yODI6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MjozMi0yODI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NTEtMjgzOjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NTEtMjgzOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRSZXN1bHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODQ6MS0yODQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjUxLTI4NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjUxLTI4NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjMyLTI4NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjEtMjg1OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTo2Mi0yODU6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTo2Mi0yODU6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTozMi0yODU6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ni0yODY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ni0yODY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHJpZ2dlclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6MS0yODc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjUzLTI4Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjUzLTI4Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjMyLTI4Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UeXBlRGVyaXZhdGlvblJ1bGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU3LTI4ODo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU3LTI4ODo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UeXBlUmVzdGZ1bEludGVyYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo0Ny0yODk6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo0Ny0yODk6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VURJRW50cnlUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjEtMjkwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDo0Ni0yOTA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDo0Ni0yOTA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDozMi0yOTA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VW5pdHNPZlRpbWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MS0yOTE6NDgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjM4LTI5MTo0OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjM4LTI5MTo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjMyLTI5MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Vc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MS0yOTI6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjQ3LTI5Mjo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjQ3LTI5Mjo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjMyLTI5Mjo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WYXJpYWJsZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MS0yOTM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjQ1LTI5Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjQ1LTI5Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjMyLTI5Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25CYXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjEtMjk0OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDo0NS0yOTQ6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDo0NS0yOTQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDozMi0yOTQ6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uRXllcyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NDctMjk1OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NDctMjk1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NjoxLTI5Njo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6NDktMjk2OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6NDktMjk2OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MzItMjk2OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVhQYXRoVXNhZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk3OjEtMjk3OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo0Ny0yOTc6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo0Ny0yOTc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzozMi0yOTc6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9YmFzZTY0QmluYXJ5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjEtMjk4OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozNy0yOTg6NDciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozNy0yOTg6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozMi0yOTg6MzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aWQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTk6MS0yOTk6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9Cb29sZWFuIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo0My0yOTk6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo0My0yOTk6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMy0yOTk6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Ym9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDoxLTMwMDo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjM3LTMwMDo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjM3LTMwMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjMwLTMwMDozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjQ1LTMwMTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjQ1LTMwMTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjM0LTMwMTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMjoxLTMwMjo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0RlY2ltYWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQzLTMwMjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQzLTMwMjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMzLTMwMjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kZWNpbWFsIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjU0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjQ0LTMwMzo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjQ0LTMwMzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjM0LTMwMzo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjEtMzA0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZWdlciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6NDMtMzA0OjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6NDMtMzA0OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MzMtMzA0OjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWludGVnZXIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDU6MS0zMDU6NTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQxLTMwNTo1MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQxLTMwNTo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjMyLTMwNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1zdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6MS0zMDY6NDciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9UaW1lIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozNy0zMDY6NDciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozNy0zMDY6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMC0zMDY6MzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dGltZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNzoxLTMwNzo0OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzgtMzA3OjQ4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzgtMzA3OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzItMzA3OjM0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXVyaSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwODoxLTMwODo1MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6NDAtMzA4OjUwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6NDAtMzA4OjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MzItMzA4OjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXhodG1sIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/DQMFHIRHelpers" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.113762.1.4.1", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.113762.1.4.1" - } - ], - "version": "20150331", - "name": "ONCAdministrativeSex", - "title": "ONC Administrative Sex", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for ONC Administrative Sex.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", - "version": "HL7V3.0_2020-11", - "code": "F", - "display": "Female" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", - "version": "HL7V3.0_2020-11", - "code": "M", - "display": "Male" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.113762.1.4.1" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "FHIRCommon", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon", - "version": "4.0.1", - "name": "FHIRCommon", - "title": "Library - FHIR Common", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "A Shared library encapsulating valuable common terminologies and functions used in FHIR-based CQL artifacts.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Code system LOINC", - "resource": "http://loinc.org" - }, - { - "type": "depends-on", - "display": "Code system SNOMEDCT", - "resource": "http://snomed.info/sct" - }, - { - "type": "depends-on", - "display": "Code system RoleCode", - "resource": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" - }, - { - "type": "depends-on", - "display": "Code system Diagnosis Role", - "resource": "http://terminology.hl7.org/CodeSystem/diagnosis-role" - }, - { - "type": "depends-on", - "display": "Code system RequestIntent", - "resource": "http://terminology.hl7.org/CodeSystem/request-intent" - }, - { - "type": "depends-on", - "display": "Code system MedicationRequestCategory", - "resource": "http://terminology.hl7.org/CodeSystem/medicationrequest-category" - }, - { - "type": "depends-on", - "display": "Code system ConditionClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" - }, - { - "type": "depends-on", - "display": "Code system ConditionVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" - }, - { - "type": "depends-on", - "display": "Code system AllergyIntoleranceClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical" - }, - { - "type": "depends-on", - "display": "Code system AllergyIntoleranceVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification" - } - ], - "parameter": [ - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBDb21tb24gdGVybWlub2xvZ2llcyBhbmQgZnVuY3Rpb25zIHVzZWQgaW4gRkhJUi1iYXNlZCBDUUwgYXJ0aWZhY3RzCiovCgpsaWJyYXJ5IEZISVJDb21tb24gdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCmNvZGVzeXN0ZW0gIkxPSU5DIjogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gIlNOT01FRENUIjogJ2h0dHA6Ly9zbm9tZWQuaW5mby9zY3QnCmNvZGVzeXN0ZW0gIlJvbGVDb2RlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vdjMtUm9sZUNvZGUnCmNvZGVzeXN0ZW0gIkRpYWdub3NpcyBSb2xlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUnCmNvZGVzeXN0ZW0gIlJlcXVlc3RJbnRlbnQiOiAnaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9yZXF1ZXN0LWludGVudCcKY29kZXN5c3RlbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL21lZGljYXRpb25yZXF1ZXN0LWNhdGVnb3J5Jwpjb2Rlc3lzdGVtICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2NvbmRpdGlvbi12ZXItc3RhdHVzJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24nCgpjb2RlICJCaXJ0aGRhdGUiOiAnMjExMTItOCcgZnJvbSAiTE9JTkMiIGRpc3BsYXkgJ0JpcnRoIGRhdGUnCmNvZGUgIkRlYWQiOiAnNDE5MDk5MDA5JyBmcm9tICJTTk9NRURDVCIgZGlzcGxheSAnRGVhZCcKY29kZSAiRVIiOiAnRVInIGZyb20gIlJvbGVDb2RlIiBkaXNwbGF5ICdFbWVyZ2VuY3kgcm9vbScKY29kZSAiSUNVIjogJ0lDVScgZnJvbSAiUm9sZUNvZGUiIGRpc3BsYXkgJ0ludGVuc2l2ZSBjYXJlIHVuaXQnCmNvZGUgIkJpbGxpbmciOiAnYmlsbGluZycgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0JpbGxpbmcnCgovLyBDb25kaXRpb24gQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWN0aXZlIjogJ2FjdGl2ZScgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKY29kZSAicmVjdXJyZW5jZSI6ICdyZWN1cnJlbmNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZWxhcHNlIjogJ3JlbGFwc2UnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCmNvZGUgImluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZW1pc3Npb24iOiAncmVtaXNzaW9uJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZXNvbHZlZCI6ICdyZXNvbHZlZCcgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKCi8vIENvbmRpdGlvbiBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAidW5jb25maXJtZWQiOiAndW5jb25maXJtZWQnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAicHJvdmlzaW9uYWwiOiAncHJvdmlzaW9uYWwnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiZGlmZmVyZW50aWFsIjogJ2RpZmZlcmVudGlhbCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJjb25maXJtZWQiOiAnY29uZmlybWVkJyBmcm9tIENvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzCmNvZGUgInJlZnV0ZWQiOiAncmVmdXRlZCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJlbnRlcmVkLWluLWVycm9yIjogJ2VudGVyZWQtaW4tZXJyb3InIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIEFsbGVyZ3kvSW50b2xlcmFuY2UgQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS1hY3RpdmUiOiAnYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LWluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LXJlc29sdmVkIjogJ3Jlc29sdmVkJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgoKLy8gQWxsZXJneS9JbnRvbGVyYW5jZSBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS11bmNvbmZpcm1lZCI6ICd1bmNvbmZpcm1lZCcgZnJvbSBBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJhbGxlcmd5LWNvbmZpcm1lZCI6ICdjb25maXJtZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiYWxsZXJneS1yZWZ1dGVkIjogJ3JlZnV0ZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIE1lZGljYXRpb25SZXF1ZXN0IENhdGVnb3J5IENvZGVzCmNvZGUgIkNvbW11bml0eSI6ICdjb21tdW5pdHknIGZyb20gIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiIGRpc3BsYXkgJ0NvbW11bml0eScKY29kZSAiRGlzY2hhcmdlIjogJ2Rpc2NoYXJnZScgZnJvbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgZGlzcGxheSAnRGlzY2hhcmdlJwoKLy8gRGlhZ25vc2lzIFJvbGUgQ29kZXMKY29kZSAiQUQiOiAnQUQnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdBZG1pc3Npb24gZGlhZ25vc2lzJwpjb2RlICJERCI6ICdERCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0Rpc2NoYXJnZSBkaWFnbm9zaXMnCmNvZGUgIkNDIjogJ0NDJyBmcm9tICJEaWFnbm9zaXMgUm9sZSIgZGlzcGxheSAnQ2hpZWYgY29tcGxhaW50Jwpjb2RlICJDTSI6ICdDTScgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0NvbW9yYmlkaXR5IGRpYWdub3NpcycKY29kZSAicHJlLW9wIjogJ3ByZS1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3ByZS1vcCBkaWFnbm9zaXMnCmNvZGUgInBvc3Qtb3AiOiAncG9zdC1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3Bvc3Qtb3AgZGlhZ25vc2lzJwpjb2RlICJiaWxsaW5nIjogJ2JpbGxpbmcnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdiaWxsaW5nIGRpYWdub3NpcycKCmNvbnRleHQgUGF0aWVudAoKCi8qCkBkZXNjcmlwdGlvbjogTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsCkBjb21tZW50OiBOb3JtYWxpemVzIGEgY2hvaWNlIHR5cGUgb2YgRkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbmNlLCBGSElSLnN0cmluZywgRkhJUi5BZ2UsIG9yIEZISVIuUmFuZ2UgdHlwZXMKdG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlCnJlcHJlc2VudGF0aW9ucyBmb3IgdGltaW5nLXZhbHVlZCBlbGVtZW50cyBpbiBGSElSLCBhbGxvd2luZyB0aGlzIGZ1bmN0aW9uIHRvIGJlIHVzZWQgYWNyb3NzIGFueSByZXNvdXJjZS4gTk9URTogRHVlIHRvIHRoZQpjb21wbGV4aXR5IG9mIGRldGVybWluaW5nIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgb3IgU3RyaW5nIHR5cGUsIHRoaXMgZnVuY3Rpb24gd2lsbCB0aHJvdyBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiBpdCBpcyB1c2VkCndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4KKi8KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwoY2hvaWNlIENob2ljZTxGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFudCwgRkhJUi5zdHJpbmcsIEZISVIuQWdlLCBGSElSLlJhbmdlPik6CiAgY2FzZQoJICB3aGVuIGNob2ljZSBpcyBGSElSLmRhdGVUaW1lIHRoZW4KICAgIAlJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNob2ljZSBhcyBGSElSLmRhdGVUaW1lKSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5kYXRlVGltZSldCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5QZXJpb2QgdGhlbgogIAkJRkhJUkhlbHBlcnMuVG9JbnRlcnZhbChjaG9pY2UgYXMgRkhJUi5QZXJpb2QpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5pbnN0YW50IHRoZW4KCQkJSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KV0KCQl3aGVuIGNob2ljZSBpcyBGSElSLkFnZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjaG9pY2UgYXMgRkhJUi5BZ2UpLAoJCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNob2ljZSBhcyBGSElSLkFnZSkgKyAxIHllYXIpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5SYW5nZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSgoY2hvaWNlIGFzIEZISVIuUmFuZ2UpLmxvdyksCgkJCSAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNob2ljZSBhcyBGSElSLlJhbmdlKS5oaWdoKSArIDEgeWVhcikKICAgIHdoZW4gY2hvaWNlIGlzIEZISVIuVGltaW5nIHRoZW4KCQkgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZScpCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLnN0cmluZyB0aGVuCiAgICAgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUnKQoJCWVsc2UKICAgICAgbnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4KCWVuZAoKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhIGxpc3Qgb2YgaW50ZXJ2YWxzIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBFdmVudCBvciBCb3VuZHMgb2YgYSBUaW1pbmcgcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuCiovCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbEZyb21MaXN0KHZhbHVlIExpc3Q8RkhJUi5kYXRlVGltZT4pOgogIHZhbHVlIHYKICAgIHJldHVybiBUb0ludGVydmFsKHYpCgpkZWZpbmUgZnVuY3Rpb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh2YWx1ZSBMaXN0PEludGVydmFsPERhdGVUaW1lPj4pOgogIGlmIHZhbHVlIGlzIG5vdCBudWxsIHRoZW4gdmFsdWUgZWxzZSB7IH0KCmRlZmluZSBmdW5jdGlvbiBUb1RpbWluZ0ludGVydmFsKHRpbWluZyBGSElSLlRpbWluZyk6CiAgY29udmVydCAoCiAgICBOdWxsVG9FbXB0eUludGVydmFsKFRvSW50ZXJ2YWxGcm9tTGlzdCh0aW1pbmcuZXZlbnQpKQogICAgdW5pb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh7VG9JbnRlcnZhbCh0aW1pbmcucmVwZWF0LmJvdW5kcyBhcyBGSElSLlBlcmlvZCl9KQogICkgdG8gTGlzdDxJbnRlcnZhbDxEYXRlVGltZT4+IGV4Y2VwdCB7IG51bGwgfQoKCi8qCkBkZXNjcmlwdGlvbjogcmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLgpAY29tbWVudDogTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmUKTk9URTogRnVuY3Rpb24gb3ZlcmxvYWRzIGluY29uc2lzdGVudGx5IGVyciBpbiBDUUwgMS41LCB0aGlzIGlzIGNvbW1lbnRlZCB1bnRpbCBhIGZpeCBpcyBmb3VuZApkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKToKICBvYnNlcnZhdGlvbiBvCiAgICByZXR1cm4gQ29hbGVzY2UoVG9JbnRlcnZhbChvLmVmZmVjdGl2ZSksIFRvSW50ZXJ2YWwoby5pc3N1ZWQpKQoqLwoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93CmEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9BYmF0ZW1lbnRJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBpZiBjb25kaXRpb24ub25zZXQgaXMgbm90IG51bGwgYW5kIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgbnVsbCBhbmQgKGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJhY3RpdmUiCiAgICBvciBjb25kaXRpb24uY2xpbmljYWxTdGF0dXMgfiAicmVjdXJyZW5jZSIKICAgIG9yIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJyZWxhcHNlIikgdGhlbgogICAgSW50ZXJ2YWxbTm93KCksIE5vdygpXQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmRhdGVUaW1lIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuZGF0ZVRpbWUpLCBGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5kYXRlVGltZSldCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5QZXJpb2QgdGhlbgoJICBGSElSSGVscGVycy5Ub0ludGVydmFsKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5QZXJpb2QpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5zdHJpbmcgdGhlbgogICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJzEnLCAnRXJyb3InLCAnQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZScpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5BZ2UgdGhlbgoJCUludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5BZ2UpLAoJCQlGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuQWdlKSArIDEgeWVhcikKCWVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLlJhbmdlIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5SYW5nZSkubG93KSwKCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KChjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuUmFuZ2UpLmhpZ2gpICsgMSB5ZWFyKQoJZWxzZSBpZiBjb25kaXRpb24uYWJhdGVtZW50IGlzIEZISVIuYm9vbGVhbiB0aGVuCgkgIEludGVydmFsW2VuZCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGNvbmRpdGlvbi5yZWNvcmRlZERhdGUpCgllbHNlIG51bGwKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbQpvbnNldCB0byBhYmF0ZW1lbnQgYXMgc3BlY2lmaWVkIGluIHRoZSBnaXZlbiBDb25kaXRpb24uCiovCmRlZmluZSBmdW5jdGlvbiBUb1ByZXZhbGVuY2VJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBJbnRlcnZhbFtzdGFydCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGVuZCBvZiBUb0FiYXRlbWVudEludGVydmFsKGNvbmRpdGlvbikpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4KQGNvbW1lbnQ6IFVzZXMgdGhlIFRvSW50ZXJ2YWwgZnVuY3Rpb24gYW5kIGxhc3RPY2N1cmVuY2UgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9QcmV2YWxlbmNlSW50ZXJ2YWwoYWxsZXJneUludG9sZXJhbmNlIEFsbGVyZ3lJbnRvbGVyYW5jZSk6CiAgSW50ZXJ2YWxbc3RhcnQgb2YgVG9JbnRlcnZhbChhbGxlcmd5SW50b2xlcmFuY2Uub25zZXQpLCBDb2FsZXNjZShhbGxlcmd5SW50b2xlcmFuY2UubGFzdE9jY3VycmVuY2UsIE5vdygpKV0KCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlCkNRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBFeHRlbnNpb24oZG9tYWluUmVzb3VyY2UgRG9tYWluUmVzb3VyY2UsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tICJFeHRlbnNpb25zIihkb21haW5SZXNvdXJjZSwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIGVsZW1lbnQuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEV4dGVuc2lvbnMoZWxlbWVudCwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZQpDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UubW9kaWZpZXJFeHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9IHVybAoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCB1cmwgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBNb2RpZmllckV4dGVuc2lvbnMoZG9tYWluUmVzb3VyY2UsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gdXJsCgkJcmV0dXJuIEUKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBNb2RpZmllckV4dGVuc2lvbihlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gTW9kaWZpZXJFeHRlbnNpb25zKGVsZW1lbnQsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MKY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5leHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9ICgnaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLycgKyBpZCkKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VFeHRlbnNpb25zKGVsZW1lbnQgRWxlbWVudCwgaWQgU3RyaW5nKToKICBlbGVtZW50LmV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZUV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIGlkIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gQmFzZUV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbihkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBCYXNlTW9kaWZpZXJFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCBCYWNrYm9uZUVsZW1lbnQsIGlkIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9uKGVsZW1lbnQgQmFja2JvbmVFbGVtZW50LCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iQ29tbW9uIHRlcm1pbm9sb2dpZXMgYW5kIGZ1bmN0aW9ucyB1c2VkIGluIEZISVItYmFzZWQgQ1FMIGFydGlmYWN0cyIvPgogICA8L2Fubm90YXRpb24+CiAgIDxpZGVudGlmaWVyIGlkPSJGSElSQ29tbW9uIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg6MS04OjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiLz4KICAgPC91c2luZ3M+CiAgIDxpbmNsdWRlcz4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDoxLTEwOjQxIiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIvPgogICA8L2luY2x1ZGVzPgogICA8Y29kZVN5c3RlbXM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjozOCIgbmFtZT0iTE9JTkMiIGlkPSJodHRwOi8vbG9pbmMub3JnIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo0NyIgbmFtZT0iU05PTUVEQ1QiIGlkPSJodHRwOi8vc25vbWVkLmluZm8vc2N0IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ6MS0xNDo3NCIgbmFtZT0iUm9sZUNvZGUiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3YzLVJvbGVDb2RlIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU6MS0xNTo4MyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2RpYWdub3Npcy1yb2xlIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY6MS0xNjo4MiIgbmFtZT0iUmVxdWVzdEludGVudCIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vcmVxdWVzdC1pbnRlbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTE3OjEwNiIgbmFtZT0iTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vbWVkaWNhdGlvbnJlcXVlc3QtY2F0ZWdvcnkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjEwMSIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk6MS0xOToxMDciIG5hbWU9IkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIiBpZD0iaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOjEtMjA6MTE5IiBuYW1lPSJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIiBpZD0iaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9hbGxlcmd5aW50b2xlcmFuY2UtY2xpbmljYWwiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIyMToxLTIxOjEyNyIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24iIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC9jb2RlU3lzdGVtcz4KICAgPGNvZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOjEtMjM6NjEiIG5hbWU9IkJpcnRoZGF0ZSIgaWQ9IjIxMTEyLTgiIGRpc3BsYXk9IkJpcnRoIGRhdGUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyMzozNC0yMzo0MCIgbmFtZT0iTE9JTkMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ6MS0yNDo1NSIgbmFtZT0iRGVhZCIgaWQ9IjQxOTA5OTAwOSIgZGlzcGxheT0iRGVhZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjI0OjMxLTI0OjQwIiBuYW1lPSJTTk9NRURDVCIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNToxLTI1OjU2IiBuYW1lPSJFUiIgaWQ9IkVSIiBkaXNwbGF5PSJFbWVyZ2VuY3kgcm9vbSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjI1OjIyLTI1OjMxIiBuYW1lPSJSb2xlQ29kZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjoxLTI2OjYzIiBuYW1lPSJJQ1UiIGlkPSJJQ1UiIGRpc3BsYXk9IkludGVuc2l2ZSBjYXJlIHVuaXQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyNjoyNC0yNjozMyIgbmFtZT0iUm9sZUNvZGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc6MS0yNzo2NSIgbmFtZT0iQmlsbGluZyIgaWQ9ImJpbGxpbmciIGRpc3BsYXk9IkJpbGxpbmciIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyNzozMi0yNzo0NyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zMDo1OSIgbmFtZT0iYWN0aXZlIiBpZD0iYWN0aXZlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzA6MzAtMzA6NTkiIG5hbWU9IkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE6MS0zMTo2NyIgbmFtZT0icmVjdXJyZW5jZSIgaWQ9InJlY3VycmVuY2UiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzMTozOC0zMTo2NyIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjoxLTMyOjYxIiBuYW1lPSJyZWxhcHNlIiBpZD0icmVsYXBzZSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjMyOjMyLTMyOjYxIiBuYW1lPSJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzOjEtMzM6NjMiIG5hbWU9ImluYWN0aXZlIiBpZD0iaW5hY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzMzozNC0zMzo2MyIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDoxLTM0OjY1IiBuYW1lPSJyZW1pc3Npb24iIGlkPSJyZW1pc3Npb24iIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzNDozNi0zNDo2NSIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTM1OjYzIiBuYW1lPSJyZXNvbHZlZCIgaWQ9InJlc29sdmVkIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzU6MzQtMzU6NjMiIG5hbWU9IkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzg6MS0zODo3MSIgbmFtZT0idW5jb25maXJtZWQiIGlkPSJ1bmNvbmZpcm1lZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjM4OjQwLTM4OjcxIiBuYW1lPSJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOToxLTM5OjcxIiBuYW1lPSJwcm92aXNpb25hbCIgaWQ9InByb3Zpc2lvbmFsIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzk6NDAtMzk6NzEiIG5hbWU9IkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDA6NzMiIG5hbWU9ImRpZmZlcmVudGlhbCIgaWQ9ImRpZmZlcmVudGlhbCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjQwOjQyLTQwOjczIiBuYW1lPSJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0MToxLTQxOjY3IiBuYW1lPSJjb25maXJtZWQiIGlkPSJjb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0MTozNi00MTo2NyIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDI6MS00Mjo2MyIgbmFtZT0icmVmdXRlZCIgaWQ9InJlZnV0ZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0MjozMi00Mjo2MyIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDM6MS00Mzo4MSIgbmFtZT0iZW50ZXJlZC1pbi1lcnJvciIgaWQ9ImVudGVyZWQtaW4tZXJyb3IiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0Mzo1MC00Mzo4MSIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDY6MS00Njo3NiIgbmFtZT0iYWxsZXJneS1hY3RpdmUiIGlkPSJhY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0NjozOC00Njo3NiIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0NzoxLTQ3OjgwIiBuYW1lPSJhbGxlcmd5LWluYWN0aXZlIiBpZD0iaW5hY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0Nzo0Mi00Nzo4MCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTQ4OjgwIiBuYW1lPSJhbGxlcmd5LXJlc29sdmVkIiBpZD0icmVzb2x2ZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0ODo0Mi00ODo4MCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjg4IiBuYW1lPSJhbGxlcmd5LXVuY29uZmlybWVkIiBpZD0idW5jb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI1MTo0OC01MTo4OCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo4NCIgbmFtZT0iYWxsZXJneS1jb25maXJtZWQiIGlkPSJjb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI1Mjo0NC01Mjo4NCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTM6MS01Mzo4MCIgbmFtZT0iYWxsZXJneS1yZWZ1dGVkIiBpZD0icmVmdXRlZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjUzOjQwLTUzOjgwIiBuYW1lPSJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NjoxLTU2OjgyIiBuYW1lPSJDb21tdW5pdHkiIGlkPSJjb21tdW5pdHkiIGRpc3BsYXk9IkNvbW11bml0eSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjU2OjM2LTU2OjYyIiBuYW1lPSJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5Ii8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6ODIiIG5hbWU9IkRpc2NoYXJnZSIgaWQ9ImRpc2NoYXJnZSIgZGlzcGxheT0iRGlzY2hhcmdlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNTc6MzYtNTc6NjIiIG5hbWU9Ik1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjA6MS02MDo2NyIgbmFtZT0iQUQiIGlkPSJBRCIgZGlzcGxheT0iQWRtaXNzaW9uIGRpYWdub3NpcyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjYwOjIyLTYwOjM3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MToxLTYxOjY3IiBuYW1lPSJERCIgaWQ9IkREIiBkaXNwbGF5PSJEaXNjaGFyZ2UgZGlhZ25vc2lzIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNjE6MjItNjE6MzciIG5hbWU9IkRpYWdub3NpcyBSb2xlIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYyOjEtNjI6NjMiIG5hbWU9IkNDIiBpZD0iQ0MiIGRpc3BsYXk9IkNoaWVmIGNvbXBsYWludCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjYyOjIyLTYyOjM3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MzoxLTYzOjY5IiBuYW1lPSJDTSIgaWQ9IkNNIiBkaXNwbGF5PSJDb21vcmJpZGl0eSBkaWFnbm9zaXMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI2MzoyMi02MzozNyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo3MiIgbmFtZT0icHJlLW9wIiBpZD0icHJlLW9wIiBkaXNwbGF5PSJwcmUtb3AgZGlhZ25vc2lzIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNjQ6MzAtNjQ6NDUiIG5hbWU9IkRpYWdub3NpcyBSb2xlIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY1OjEtNjU6NzUiIG5hbWU9InBvc3Qtb3AiIGlkPSJwb3N0LW9wIiBkaXNwbGF5PSJwb3N0LW9wIGRpYWdub3NpcyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjY1OjMyLTY1OjQ3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NjoxLTY2Ojc1IiBuYW1lPSJiaWxsaW5nIiBpZD0iYmlsbGluZyIgZGlzcGxheT0iYmlsbGluZyBkaWFnbm9zaXMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI2NjozMi02Njo0NyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgIDwvY29kZXM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSI2ODoxLTY4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY4OjEtNjg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTk5OjQiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5vcm1hbGl6ZXMgYSBjaG9pY2UgdHlwZSBvZiBGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFuY2UsIEZISVIuc3RyaW5nLCBGSElSLkFnZSwgb3IgRkhJUi5SYW5nZSB0eXBlcyYjeGE7dG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlJiN4YTtyZXByZXNlbnRhdGlvbnMgZm9yIHRpbWluZy12YWx1ZWQgZWxlbWVudHMgaW4gRkhJUiwgYWxsb3dpbmcgdGhpcyBmdW5jdGlvbiB0byBiZSB1c2VkIGFjcm9zcyBhbnkgcmVzb3VyY2UuIE5PVEU6IER1ZSB0byB0aGUmI3hhO2NvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYSBzaW5nbGUgaW50ZXJ2YWwgZnJvbSBhIFRpbWluZyBvciBTdHJpbmcgdHlwZSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIGl0IGlzIHVzZWQmI3hhO3dpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDozLTk5OjQiIHhzaTp0eXBlPSJDYXNlIj4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjgxOjQtODI6MTExIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iODE6OS04MTozMSIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODE6OS04MToxNCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgxOjE5LTgxOjMxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvd2hlbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iODI6Ni04MjoxMTEiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MjoxNS04Mjo2MSIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4MjozOC04Mjo2MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODI6MzgtODI6NDMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4Mjo0OC04Mjo2MCIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI4Mjo2NC04MjoxMTAiIG5hbWU9IlRvRGF0ZVRpbWUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODI6ODctODI6MTA5IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4Mjo4Ny04Mjo5MiIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjk3LTgyOjEwOSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iODM6My04NDo0OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjgzOjgtODM6MjgiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjgtODM6MTMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4MzoxOC04MzoyOCIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI4NDo1LTg0OjQ5IiBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg0OjI4LTg0OjQ4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4NDoyOC04NDozMyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg0OjM4LTg0OjQ4IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9Ijg1OjMtODY6MTA3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iODU6OC04NToyOSIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODU6OC04NToxMyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg1OjE4LTg1OjI5IiBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI4Njo0LTg2OjEwNyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg2OjEzLTg2OjU4IiBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg2OjM2LTg2OjU3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4NjozNi04Njo0MSIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg2OjQ2LTg2OjU3IiBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODY6NjEtODY6MTA2IiBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg2Ojg0LTg2OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODY6ODQtODY6ODkiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4Njo5NC04NjoxMDUiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iODc6My04OTo5NyI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9Ijg3OjgtODc6MjUiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg3OjgtODc6MTMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NzoxOC04NzoyNSIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjUtODk6OTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iODg6MTQtODg6OTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODoxNC04ODo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjMzLTg4OjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODg6MzMtODg6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo1NC04ODo5NSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo3Ny04ODo5NCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6ODIiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4ODo4Ny04ODo5NCIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI4OTo2LTg5Ojk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6Ni04OTo4NyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6NDIiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OToyNS04OTo0MSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg5OjI1LTg5OjMxIiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NDYtODk6ODciIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NjktODk6ODYiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojc0IiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6NzktODk6ODYiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjkxLTg5Ojk2IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxsb3dDbG9zZWRFeHByZXNzaW9uIHBhdGg9Imxvd0Nsb3NlZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1LTg5Ojk3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg4OjE0LTg4Ojk1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MTQtODg6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODozMy04ODo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjMzLTg4OjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NTQtODg6OTUiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6OTQiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4OjgyIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6ODctODg6OTQiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODk6Ni04OTo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6ODciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5OjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6MjUtODk6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OToyNS04OTozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjQ2LTg5Ojg3IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojg2IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo3NCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg5Ojc5LTg5Ojg2IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo5MS04OTo5NiIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvd0Nsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgIDxoaWdoIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1LTg5Ojk3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg4OjE0LTg4Ojk1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MTQtODg6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODozMy04ODo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjMzLTg4OjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NTQtODg6OTUiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6OTQiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4OjgyIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6ODctODg6OTQiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODk6Ni04OTo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6ODciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5OjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6MjUtODk6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OToyNS04OTozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjQ2LTg5Ojg3IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojg2IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo3NCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg5Ojc5LTg5Ojg2IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo5MS04OTo5NiIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODg6NS04OTo5NyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4ODoxNC04ODo5NSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjE0LTg4OjUwIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MzMtODg6NDkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODozMy04ODozOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjU0LTg4Ojk1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4Ojk0IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo3Ny04ODo4MiIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg4Ojg3LTg4Ojk0IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9Ijg5OjYtODk6OTYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5Ojg3IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6Ni04OTo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjI1LTg5OjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODk6MjUtODk6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo0Ni04OTo4NyIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo4NiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NjktODk6NzQiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4OTo3OS04OTo4NiIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6OTEtODk6OTYiIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9oaWdoQ2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTA6My05MjoxMDYiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5MDo4LTkwOjI3IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo4LTkwOjEzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTA6MTgtOTA6MjciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjUtOTI6MTA2IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjkxOjE0LTkxOjEwMyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjE0LTkxOjUwIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6MzMtOTE6NDkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTozMy05MTozOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjU0LTkxOjEwMyIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo3Ny05MToxMDIiIHBhdGg9ImxvdyIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo3Ny05MTo5OCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzgtOTE6ODMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MTo4OC05MTo5NyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Mjo2LTkyOjEwNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjYtOTI6OTYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MjUtOTI6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MjoyNS05MjozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjQ2LTkyOjk2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjY5LTkyOjk1IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkyOjY5LTkyOjkwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo3MC05Mjo3NSIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjgwLTkyOjg5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MjoxMDAtOTI6MTA1IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxsb3dDbG9zZWRFeHByZXNzaW9uIHBhdGg9Imxvd0Nsb3NlZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1LTkyOjEwNiIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5MToxNC05MToxMDMiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MToxNC05MTo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjMzLTkxOjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6MzMtOTE6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo1NC05MToxMDMiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzctOTE6MTAyIiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NzctOTE6OTgiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc4LTkxOjgzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6ODgtOTE6OTciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTI6Ni05MjoxMDUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjI1LTkyOjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6MjUtOTI6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo0Ni05Mjo5NiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2OS05Mjo5NSIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mjo2OS05Mjo5MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NzAtOTI6NzUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5Mjo4MC05Mjo4OSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MTAwLTkyOjEwNSIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvd0Nsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgIDxoaWdoIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1LTkyOjEwNiIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5MToxNC05MToxMDMiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MToxNC05MTo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjMzLTkxOjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6MzMtOTE6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo1NC05MToxMDMiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzctOTE6MTAyIiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NzctOTE6OTgiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc4LTkxOjgzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6ODgtOTE6OTciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTI6Ni05MjoxMDUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjI1LTkyOjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6MjUtOTI6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo0Ni05Mjo5NiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2OS05Mjo5NSIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mjo2OS05Mjo5MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NzAtOTI6NzUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5Mjo4MC05Mjo4OSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MTAwLTkyOjEwNSIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NS05MjoxMDYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTE6MTQtOTE6MTAzIiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6MTQtOTE6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTozMy05MTo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjMzLTkxOjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NTQtOTE6MTAzIiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc3LTkxOjEwMiIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjc3LTkxOjk4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo3OC05MTo4MyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkxOjg4LTkxOjk3IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjkyOjYtOTI6MTA1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjYtOTI6NDIiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MjoyNS05Mjo0MSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkyOjI1LTkyOjMxIiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NDYtOTI6OTYiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NjktOTI6OTUiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NjktOTI6OTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjcwLTkyOjc1IiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTI6ODAtOTI6ODkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjEwMC05MjoxMDUiIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9oaWdoQ2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTM6NS05NDoxMTQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5MzoxMC05MzozMCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTM6MTAtOTM6MTUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MzoyMC05MzozMCIgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI5NDo1LTk0OjExNCIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk0OjEzLTk0OjM4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NDoxMy05NDoxNiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NDoyMS05NDozOCIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTQ6MzAtOTQ6MzciIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk0OjQxLTk0OjQ0IiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9Ijk0OjQ3LTk0OjQ5IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9Ijk0OjUyLTk0OjU4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9Ijk0OjYxLTk0OjExMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkNhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTU6NS05NjoxMTEiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5NToxMC05NTozMCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTU6MTAtOTU6MTUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NToyMC05NTozMCIgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI5Njo3LTk2OjExMSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjE1LTk2OjQwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NjoxNS05NjoxOCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjoyMy05Njo0MCIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTY6MzItOTY6MzkiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk2OjQzLTk2OjQ2IiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9Ijk2OjQ5LTk2OjUxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9Ijk2OjU0LTk2OjYwIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9Ijk2OjYzLTk2OjExMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkNhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTg6Ny05ODozMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTg6Ny05ODoxMCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5ODoxNS05ODozMiIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTg6MjQtOTg6MzEiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjaG9pY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzk6MzUtNzk6MTMwIiB4c2k6dHlwZT0iQ2hvaWNlVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6NDItNzk6NTQiIG5hbWU9ImZoaXI6ZGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo1Ny03OTo2NyIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo3MC03OTo4MCIgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo4My03OTo5NCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6OTctNzk6MTA3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8Y2hvaWNlIGxvY2F0b3I9Ijc5OjExMC03OToxMTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6MTIwLTc5OjEyOSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICA8L29wZXJhbmRUeXBlU3BlY2lmaWVyPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNzoxLTEwOToyNCIgbmFtZT0iVG9JbnRlcnZhbEZyb21MaXN0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYSBsaXN0IG9mIGludGVydmFscyByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgRXZlbnQgb3IgQm91bmRzIG9mIGEgVGltaW5nIHJlc291cmNlLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA4OjMtMTA5OjI0IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwODozLTEwODo5IiBhbGlhcz0idiI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwODozLTEwODo3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iMTA5OjUtMTA5OjI0Ij4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA5OjEyLTEwOToyNCIgbmFtZT0iVG9JbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTA5OjIzIiBuYW1lPSJ2IiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iQ2hvaWNlVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNzo0Mi0xMDc6NjAiIHhzaTp0eXBlPSJMaXN0VHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgIDxlbGVtZW50VHlwZSBsb2NhdG9yPSIxMDc6NDctMTA3OjU5IiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZFR5cGVTcGVjaWZpZXI+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTExOjEtMTEyOjQyIiBuYW1lPSJOdWxsVG9FbXB0eUludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTI6My0xMTI6NDIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEyOjYtMTEyOjIyIiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEyOjYtMTEyOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEyOjYtMTEyOjEwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTEyOjI5LTExMjozMyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjExMjo0MC0xMTI6NDIiIHhzaTp0eXBlPSJMaXN0Ii8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTExOjQzLTExMTo2NiIgeHNpOnR5cGU9Ikxpc3RUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgPGVsZW1lbnRUeXBlIGxvY2F0b3I9IjExMTo0OC0xMTE6NjUiIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2F0b3I9IjExMTo1Ny0xMTE6NjQiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9lbGVtZW50VHlwZT4KICAgICAgICAgICAgPC9vcGVyYW5kVHlwZVNwZWNpZmllcj4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTQ6MS0xMTg6NDciIG5hbWU9IlRvVGltaW5nSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTozLTExODo0NyIgeHNpOnR5cGU9IkV4Y2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExNTozLTExODozMSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iWCI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNToxMS0xMTg6MyIgeHNpOnR5cGU9IlVuaW9uIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE2OjUtMTE2OjU3IiBuYW1lPSJOdWxsVG9FbXB0eUludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTY6MjUtMTE2OjU2IiBuYW1lPSJUb0ludGVydmFsRnJvbUxpc3QiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExNjo0NC0xMTY6NTUiIHBhdGg9ImV2ZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNjo0NC0xMTY6NDkiIG5hbWU9InRpbWluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE3OjExLTExNzo4MCIgbmFtZT0iTnVsbFRvRW1wdHlJbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE3OjMxLTExNzo3OSIgeHNpOnR5cGU9Ikxpc3QiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhdG9yPSIxMTc6MzItMTE3Ojc4IiBuYW1lPSJUb0ludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTc6NDMtMTE3Ojc3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTc6NDMtMTE3OjYyIiBwYXRoPSJib3VuZHMiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE3OjQzLTExNzo1NSIgcGF0aD0icmVwZWF0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0My0xMTc6NDgiIG5hbWU9InRpbWluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTc6NjctMTE3Ojc3IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkNob2ljZVR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6ZGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgPHJldHVybiBkaXN0aW5jdD0iZmFsc2UiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJYIiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgYWxpYXM9IlgiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDAtMTE4OjQ3IiB4c2k6dHlwZT0iTGlzdCI+CiAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2F0b3I9IjExODo0Mi0xMTg6NDUiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDxyZXR1cm4gZGlzdGluY3Q9ImZhbHNlIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iWCIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InRpbWluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTQ6NDEtMTE0OjUxIiBuYW1lPSJmaGlyOlRpbWluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNToxLTE1NDoxMCIgbmFtZT0iVG9BYmF0ZW1lbnRJbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJyZXR1cm5zIHRoZSBleGlzdGluZyBwb3NzaWJsZSB0aW1lc3RhbXAgZm9yIG9ic2VydmF0aW9uIHJlc291cmNlcywgcHJlZmVycmluZyBlZmZlY3RpdmUgb3ZlciBpc3N1ZWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IG5laXRoZXIgZWZmZWN0aXZlIG5vciBpc3N1ZWQgYXJlIHJlcXVpcmVkIGZpZWxkcyBpbiBRSS1Db3JlJiN4YTtOT1RFOiBGdW5jdGlvbiBvdmVybG9hZHMgaW5jb25zaXN0ZW50bHkgZXJyIGluIENRTCAxLjUsIHRoaXMgaXMgY29tbWVudGVkIHVudGlsIGEgZml4IGlzIGZvdW5kJiN4YTtkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKTomI3hhOyAgb2JzZXJ2YXRpb24gbyYjeGE7ICAgIHJldHVybiBDb2FsZXNjZShUb0ludGVydmFsKG8uZWZmZWN0aXZlKSwgVG9JbnRlcnZhbChvLmlzc3VlZCkpIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IER1ZSB0byB0aGUgY29tcGxleGl0eSBvZiBkZXRlcm1pbmluZyBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nLCB0aGlzIGZ1bmN0aW9uIHdpbGwgdGhyb3cmI3hhO2EgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzNjozLTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMzY6Ni0xMzg6NDQiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6NjQiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MzIiIHhzaTp0eXBlPSJOb3QiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MzIiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MjAiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjo2LTEzNjoxNCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6MzgtMTM2OjY0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjM4LTEzNjo1NiIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjozOC0xMzY6NDYiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcwLTEzODo0NCIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcxLTEzNzo0NiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcxLTEzNjoxMDUiIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9Db25jZXB0IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzNjo3MS0xMzY6OTQiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjo3MS0xMzY6NzkiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0NvbmNlcHQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6OTgtMTM2OjEwNSIgbmFtZT0iYWN0aXZlIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM3OjgtMTM3OjQ2IiB4c2k6dHlwZT0iRXF1aXZhbGVudCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvQ29uY2VwdCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzc6OC0xMzc6MzEiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNzo4LTEzNzoxNiIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlRvQ29uY2VwdCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzNzozNS0xMzc6NDYiIG5hbWU9InJlY3VycmVuY2UiIHhzaTp0eXBlPSJDb2RlUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzODo4LTEzODo0MyIgeHNpOnR5cGU9IkVxdWl2YWxlbnQiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0NvbmNlcHQiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM4OjgtMTM4OjMxIiBwYXRoPSJjbGluaWNhbFN0YXR1cyIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6OC0xMzg6MTYiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0NvbmNlcHQiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzg6MzUtMTM4OjQzIiBuYW1lPSJyZWxhcHNlIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTM5OjUtMTM5OjI2IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTM5OjE0LTEzOToxOCIgeHNpOnR5cGU9Ik5vdyIvPgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxMzk6MjEtMTM5OjI1IiB4c2k6dHlwZT0iTm93Ii8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTQwOjgtMTU0OjEwIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0MDoxMS0xNDA6NDYiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0MDoxMS0xNDA6MjkiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6MTEtMTQwOjE5IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzQtMTQwOjQ2IiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIxNDE6NC0xNDE6MTM1IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQxOjEzLTE0MTo3MiIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6MzYtMTQxOjcxIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6MzYtMTQxOjU0IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQxOjM2LTE0MTo0NCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQxOjU5LTE0MTo3MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxNDE6NzUtMTQxOjEzNCIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6OTgtMTQxOjEzMyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQxOjk4LTE0MToxMTYiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6OTgtMTQxOjEwNiIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQxOjEyMS0xNDE6MTMzIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE0Mjo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDI6MTAtMTQyOjQzIiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDI6MTAtMTQyOjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQyOjEwLTE0MjoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQyOjMzLTE0Mjo0MyIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjE0Mzo0LTE0Mzo2MSIgbmFtZT0iVG9JbnRlcnZhbCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDM6MjctMTQzOjYwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDM6MjctMTQzOjQ1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQzOjI3LTE0MzozNSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjUwLTE0Mzo2MCIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE0NDo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDQ6MTAtMTQ0OjQzIiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDQ6MTAtMTQ0OjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ0OjEwLTE0NDoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDo0MyIgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjE0NTo1LTE0NToxMDkiIHhzaTp0eXBlPSJNZXNzYWdlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6MTMtMTQ1OjM4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDU6MTMtMTQ1OjE2IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NToyMS0xNDU6MzgiIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2F0b3I9IjE0NTozMC0xNDU6MzciIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0NTo0MS0xNDU6NDQiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iMTQ1OjQ3LTE0NTo0OSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSIxNDU6NTItMTQ1OjU4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjE0NTo2MS0xNDU6MTA4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxNDY6Ny0xNTQ6MTAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTQ2OjEwLTE0Njo0MCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ2OjEwLTE0NjoyOCIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NjoxMC0xNDY6MTgiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NjozMy0xNDY6NDAiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozLTE0ODoxMDgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQ3OjEyLTE0NzoxMDYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MTItMTQ3OjQ4IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3OjMxLTE0Nzo0NyIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozMS0xNDc6MzciIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NTItMTQ3OjEwNiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo5MyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Nzo3NS0xNDc6ODMiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Nzo5OC0xNDc6MTA1IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE0ODo0LTE0ODoxMDciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6OTgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6NDAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6MjMtMTQ4OjM5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjIzLTE0ODoyOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0NC0xNDg6OTgiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo5NyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo4NSIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0ODo2Ny0xNDg6NzUiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0ODo5MC0xNDg6OTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoxMDItMTQ4OjEwNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93Q2xvc2VkRXhwcmVzc2lvbiBwYXRoPSJsb3dDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMtMTQ4OjEwOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNDc6MTItMTQ3OjEwNiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0NzoxMi0xNDc6NDgiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MzEtMTQ3OjQ3IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMxLTE0NzozNyIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo1Mi0xNDc6MTA2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo3NS0xNDc6MTA1IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjkzIiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo4MyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ3Ojk4LTE0NzoxMDUiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTQ4OjQtMTQ4OjEwNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo5OCIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo0MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoyMy0xNDg6MzkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6MjMtMTQ4OjI5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjQ0LTE0ODo5OCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojk3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojg1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjY3LTE0ODo3NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjkwLTE0ODo5NyIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjEwMi0xNDg6MTA3IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Q2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggeHNpOnR5cGU9IlRvRGF0ZVRpbWUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozLTE0ODoxMDgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQ3OjEyLTE0NzoxMDYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MTItMTQ3OjQ4IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3OjMxLTE0Nzo0NyIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozMS0xNDc6MzciIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NTItMTQ3OjEwNiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo5MyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Nzo3NS0xNDc6ODMiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Nzo5OC0xNDc6MTA1IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE0ODo0LTE0ODoxMDciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6OTgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6NDAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6MjMtMTQ4OjM5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjIzLTE0ODoyOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0NC0xNDg6OTgiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo5NyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo4NSIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0ODo2Ny0xNDg6NzUiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0ODo5MC0xNDg6OTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoxMDItMTQ4OjEwNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMtMTQ4OjEwOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNDc6MTItMTQ3OjEwNiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0NzoxMi0xNDc6NDgiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MzEtMTQ3OjQ3IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMxLTE0NzozNyIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo1Mi0xNDc6MTA2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo3NS0xNDc6MTA1IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjkzIiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo4MyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ3Ojk4LTE0NzoxMDUiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTQ4OjQtMTQ4OjEwNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo5OCIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo0MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoyMy0xNDg6MzkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6MjMtMTQ4OjI5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjQ0LTE0ODo5OCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojk3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojg1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjY3LTE0ODo3NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjkwLTE0ODo5NyIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjEwMi0xNDg6MTA3IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaENsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTQ5OjctMTU0OjEwIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0OToxMC0xNDk6NDIiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0OToxMC0xNDk6MjgiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6MTAtMTQ5OjE4IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzMtMTQ5OjQyIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo0LTE1MToxMTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUwOjEzLTE1MDoxMTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MTMtMTUwOjQ5IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUwOjMyLTE1MDo0OCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDozMi0xNTA6MzgiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NTMtMTUwOjExNSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzYtMTUwOjExNCIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTEwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzctMTUwOjk1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc3LTE1MDo4NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjEwMC0xNTA6MTA5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE1MTo1LTE1MToxMTciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6MTA4IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjUtMTUxOjQxIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjI0LTE1MTo0MCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MToyNC0xNTE6MzAiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NDUtMTUxOjEwOCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwNyIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjY5LTE1MTo4NyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OS0xNTE6NzciIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTo5Mi0xNTE6MTAxIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MTEyLTE1MToxMTciIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvd0Nsb3NlZEV4cHJlc3Npb24gcGF0aD0ibG93Q2xvc2VkIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo0LTE1MToxMTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUwOjEzLTE1MDoxMTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MTMtMTUwOjQ5IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUwOjMyLTE1MDo0OCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDozMi0xNTA6MzgiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NTMtMTUwOjExNSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzYtMTUwOjExNCIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTEwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzctMTUwOjk1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc3LTE1MDo4NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjEwMC0xNTA6MTA5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE1MTo1LTE1MToxMTciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6MTA4IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjUtMTUxOjQxIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjI0LTE1MTo0MCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MToyNC0xNTE6MzAiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NDUtMTUxOjEwOCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwNyIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjY5LTE1MTo4NyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OS0xNTE6NzciIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTo5Mi0xNTE6MTAxIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MTEyLTE1MToxMTciIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3dDbG9zZWRFeHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCB4c2k6dHlwZT0iVG9EYXRlVGltZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQtMTUxOjExOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNTA6MTMtMTUwOjExNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDoxMy0xNTA6NDkiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MzItMTUwOjQ4IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjMyLTE1MDozOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo1My0xNTA6MTE1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTE0IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc2LTE1MDoxMTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ny0xNTA6OTUiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTA6NzctMTUwOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTA6MTAwLTE1MDoxMDkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTUxOjUtMTUxOjExNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo1LTE1MToxMDgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6NDEiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MjQtMTUxOjQwIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjI0LTE1MTozMCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo0NS0xNTE6MTA4IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTA3IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTAyIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjktMTUxOjg3IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjY5LTE1MTo3NyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjkyLTE1MToxMDEiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MToxMTItMTUxOjExNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQtMTUxOjExOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNTA6MTMtMTUwOjExNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDoxMy0xNTA6NDkiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MzItMTUwOjQ4IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjMyLTE1MDozOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo1My0xNTA6MTE1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTE0IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc2LTE1MDoxMTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ny0xNTA6OTUiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTA6NzctMTUwOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTA6MTAwLTE1MDoxMDkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTUxOjUtMTUxOjExNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo1LTE1MToxMDgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6NDEiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MjQtMTUxOjQwIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjI0LTE1MTozMCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo0NS0xNTE6MTA4IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTA3IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTAyIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjktMTUxOjg3IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjY5LTE1MTo3NyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjkyLTE1MToxMDEiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MToxMTItMTUxOjExNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2hDbG9zZWRFeHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE1Mjo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNTI6MTAtMTUyOjQ0IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTI6MTAtMTUyOjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjEwLTE1MjoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMzLTE1Mjo0NCIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIxNTM6NC0xNTM6NzEiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUzOjEzLTE1Mzo0NiIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MzoyMC0xNTM6NDYiIG5hbWU9IlRvSW50ZXJ2YWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MzozMS0xNTM6NDUiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MzozMS0xNTM6MzkiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1Mzo0OS0xNTM6NzAiIHBhdGg9InJlY29yZGVkRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTM6NDktMTUzOjU3IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxlbHNlIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1NDo3LTE1NDoxMCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uZGl0aW9uIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0Ny0xMzU6NTUiIG5hbWU9ImZoaXI6Q29uZGl0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYxOjEtMTYyOjg3IiBuYW1lPSJUb1ByZXZhbGVuY2VJbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBwcmV2YWxlbmNlIHBlcmlvZCBvZiBhIGdpdmVuIENvbmRpdGlvbiByZXNvdXJjZS4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVXNlcyB0aGUgVG9JbnRlcnZhbCBhbmQgVG9BYmF0ZW1lbnRJbnRlcnZhbCBmdW5jdGlvbnMgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsIGZyb20mI3hhO29uc2V0IHRvIGFiYXRlbWVudCBhcyBzcGVjaWZpZWQgaW4gdGhlIGdpdmVuIENvbmRpdGlvbi4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6My0xNjI6ODciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTYyOjEyLTE2Mjo0NyIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTYyOjIxLTE2Mjo0NyIgbmFtZT0iVG9JbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTYyOjMyLTE2Mjo0NiIgcGF0aD0ib25zZXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYyOjMyLTE2Mjo0MCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE2Mjo1MC0xNjI6ODYiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjI6NTctMTYyOjg2IiBuYW1lPSJUb0FiYXRlbWVudEludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjI6NzctMTYyOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmRpdGlvbiI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjE6NDgtMTYxOjU2IiBuYW1lPSJmaGlyOkNvbmRpdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2ODoxLTE2OToxMDkiIG5hbWU9IlRvUHJldmFsZW5jZUludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVXNlcyB0aGUgVG9JbnRlcnZhbCBmdW5jdGlvbiBhbmQgbGFzdE9jY3VyZW5jZSB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY5OjMtMTY5OjEwOSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE2OToxMi0xNjk6NTYiIHhzaTp0eXBlPSJTdGFydCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE2OToyMS0xNjk6NTYiIG5hbWU9IlRvSW50ZXJ2YWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE2OTozMi0xNjk6NTUiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTozMi0xNjk6NDkiIG5hbWU9ImFsbGVyZ3lJbnRvbGVyYW5jZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxNjk6NTktMTY5OjEwOCIgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjk6NjgtMTY5OjEwMCIgcGF0aD0ibGFzdE9jY3VycmVuY2UiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY5OjY4LTE2OTo4NSIgbmFtZT0iYWxsZXJneUludG9sZXJhbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTY5OjEwMy0xNjk6MTA3IiB4c2k6dHlwZT0iTm93Ii8+CiAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJhbGxlcmd5SW50b2xlcmFuY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjU3LTE2ODo3NCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xODE6MTAiIG5hbWU9IkV4dGVuc2lvbnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyBhbnkgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUmI3hhO0NRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3OTozLTE4MToxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6My0xNzk6MjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc5OjMtMTc5OjI2IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIxODA6NC0xODA6MjAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4MDoxMC0xODA6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4MDoxOC0xODA6MjAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxODE6My0xODE6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJkb21haW5SZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6NDMtMTc4OjU2IiBuYW1lPSJmaGlyOkRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3ODo2My0xNzg6NjgiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg5OjUwIiBuYW1lPSJFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTozLTE4OTo1MCIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODk6MTgtMTg5OjUwIiBuYW1lPSJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODk6MzEtMTg5OjQ0IiBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg5OjQ3LTE4OTo0OSIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjQyLTE4ODo1NSIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InVybCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODg6NjItMTg4OjY3IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NjoxLTE5OToxMCIgbmFtZT0iRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MmI3hhO2NvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NzozLTE5OToxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTc6My0xOTc6MjEiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjMtMTk3OjE5IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZWxlbWVudCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjE5ODo0LTE5ODoyMCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTk4OjEwLTE5ODoxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTk4OjE4LTE5ODoyMCIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjE5OTozLTE5OToxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OToxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjM2LTE5Njo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5Njo0OS0xOTY6NTQiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA2OjEtMjA3OjQxIiBuYW1lPSJFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZSYjeGE7aXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjMtMjA3OjQxIiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzoxOC0yMDc6NDEiIG5hbWU9IkV4dGVuc2lvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzoyOS0yMDc6MzUiIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzozOC0yMDc6NDAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA2OjM1LTIwNjo0MSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNjo0OC0yMDY6NTMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE1OjEtMjE4OjEwIiBuYW1lPSJNb2RpZmllckV4dGVuc2lvbnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUmI3hhO0NRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNjozLTIxODoxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6My0yMTY6MzYiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjMtMjE2OjM0IiBwYXRoPSJtb2RpZmllckV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjIxNzo0LTIxNzoyMCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE3OjEwLTIxNzoxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE3OjE4LTIxNzoyMCIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjIxODozLTIxODoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImRvbWFpblJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTo1MS0yMTU6NjQiIG5hbWU9ImZoaXI6RG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE1OjcxLTIxNTo3NiIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjY6NTYiIG5hbWU9Ik1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6My0yMjY6NTYiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI2OjE4LTIyNjo1NiIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyMjY6MzctMjI2OjUwIiBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI2OjUzLTIyNjo1NSIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI1OjUwLTIyNTo2MyIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InVybCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6NzAtMjI1Ojc1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzNjoxMCIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW55IG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjMtMjM2OjEwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDozLTIzNDoyOSIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzQ6My0yMzQ6MjciIHBhdGg9Im1vZGlmaWVyRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIyMzU6NC0yMzU6MjAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIzNToxMC0yMzU6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIzNToxOC0yMzU6MjAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyMzY6My0yMzY6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzo0NC0yMzM6NTgiIG5hbWU9ImZoaXI6QmFja2JvbmVFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzo2NS0yMzM6NzAiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQzOjEtMjQ0OjQ5IiBuYW1lPSJNb2RpZmllckV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIHRoZSBzaW5nbGUgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDQ6My0yNDQ6NDkiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ0OjE4LTI0NDo0OSIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNDQ6MzctMjQ0OjQzIiBuYW1lPSJlbGVtZW50IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNDQ6NDYtMjQ0OjQ4IiBuYW1lPSJ1cmwiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0Mzo0My0yNDM6NTciIG5hbWU9ImZoaXI6QmFja2JvbmVFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0Mzo2NC0yNDM6NjkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUxOjEtMjU0OjEwIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzJiN4YTtjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTI6My0yNTQ6MTAiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjMtMjUyOjI4IiBhbGlhcz0iRSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MjozLTI1MjoyNiIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjUzOjQtMjUzOjY2IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTAtMjUzOjE0IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTgtMjUzOjY2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTktMjUzOjYwIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjUzOjY0LTI1Mzo2NSIgbmFtZT0iaWQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNTQ6My0yNTQ6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTQ6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJkb21haW5SZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTE6NDctMjUxOjYwIiBuYW1lPSJmaGlyOkRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUxOjY2LTI1MTo3MSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjE6MS0yNjI6NTEiIG5hbWU9IkJhc2VFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGJhc2UtRkhJUiBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MjozLTI2Mjo1MSIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjI6MTgtMjYyOjUxIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjYyOjMzLTI2Mjo0NiIgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTAiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjQ2LTI2MTo1OSIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTo2NS0yNjE6NzAiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjcyOjEwIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MmI3hhO2NvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDozLTI3MjoxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6My0yNzA6MjEiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcwOjMtMjcwOjE5IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZWxlbWVudCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjI3MTo0LTI3MTo2NiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjEwLTI3MToxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjE4LTI3MTo2NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjE5LTI3MTo2MCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi8iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3MTo2NC0yNzE6NjUiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iMjcyOjMtMjcyOjEwIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjEwIiBuYW1lPSJFIiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjk6NDAtMjY5OjQ2IiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjk6NTItMjY5OjU3IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3OToxLTI4MDo0NCIgbmFtZT0iQmFzZUV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6My0yODA6NDQiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjgwOjE4LTI4MDo0NCIgbmFtZT0iQmFzZUV4dGVuc2lvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4MDozMy0yODA6MzkiIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4MDo0Mi0yODA6NDMiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzktMjc5OjQ1IiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6NTEtMjc5OjU2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NzoxLTI5MDoxMCIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzJiN4YTtjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODg6My0yOTA6MTAiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg4OjMtMjg4OjM2IiBhbGlhcz0iRSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODozLTI4ODozNCIgcGF0aD0ibW9kaWZpZXJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIyODk6NC0yODk6NjYiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxMC0yODk6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxOC0yODk6NjYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxOS0yODk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyODk6NjQtMjg5OjY1IiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI5MDozLTI5MDoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MDoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImRvbWFpblJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4Nzo1NS0yODc6NjgiIG5hbWU9ImZoaXI6RG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6NzQtMjg3Ojc5IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NzoxLTI5ODo1OSIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTg6My0yOTg6NTkiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjk4OjE4LTI5ODo1OSIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjk4OjQxLTI5ODo1NCIgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI5ODo1Ny0yOTg6NTgiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjU0LTI5Nzo2NyIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5Nzo3My0yOTc6NzgiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA4OjEwIiBuYW1lPSJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA2OjMtMzA4OjEwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwNjozLTMwNjoyOSIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6My0zMDY6MjciIHBhdGg9Im1vZGlmaWVyRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMDc6NC0zMDc6NjYiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxMC0zMDc6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxOC0zMDc6NjYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxOS0zMDc6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDc6NjQtMzA3OjY1IiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMwODozLTMwODoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjQ4LTMwNTo2MiIgbmFtZT0iZmhpcjpCYWNrYm9uZUVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6NjgtMzA1OjczIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNjo1MiIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNjozLTMxNjo1MiIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6MTgtMzE2OjUyIiBuYW1lPSJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6NDEtMzE2OjQ3IiBuYW1lPSJlbGVtZW50IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6NTAtMzE2OjUxIiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE1OjQ3LTMxNTo2MSIgbmFtZT0iZmhpcjpCYWNrYm9uZUVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTU6NjctMzE1OjcyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIkNvbW1vbiB0ZXJtaW5vbG9naWVzIGFuZCBmdW5jdGlvbnMgdXNlZCBpbiBGSElSLWJhc2VkIENRTCBhcnRpZmFjdHMiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSQ29tbW9uIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iLAogICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAiaW5jbHVkZXMiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA6MS0xMDo0MSIsCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgInBhdGgiIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvZGVTeXN0ZW1zIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MzgiLAogICAgICAgICAgICAibmFtZSIgOiAiTE9JTkMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9sb2luYy5vcmciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTTk9NRURDVCIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Nub21lZC5pbmZvL3NjdCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDoxLTE0Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlJvbGVDb2RlIiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3YzLVJvbGVDb2RlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjEtMTU6ODMiLAogICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY6MS0xNjo4MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3JlcXVlc3QtaW50ZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6MTA2IiwKICAgICAgICAgICAgIm5hbWUiIDogIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vbWVkaWNhdGlvbnJlcXVlc3QtY2F0ZWdvcnkiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MS0xODoxMDEiLAogICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tY2xpbmljYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk6MS0xOToxMDciLAogICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLXZlci1zdGF0dXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MS0yMDoxMTkiLAogICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9hbGxlcmd5aW50b2xlcmFuY2UtY2xpbmljYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MS0yMToxMjciLAogICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLXZlcmlmaWNhdGlvbiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEtMjM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiQmlydGhkYXRlIiwKICAgICAgICAgICAgImlkIiA6ICIyMTExMi04IiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkJpcnRoIGRhdGUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjM0LTIzOjQwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkxPSU5DIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjEtMjQ6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiRGVhZCIsCiAgICAgICAgICAgICJpZCIgOiAiNDE5MDk5MDA5IiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkRlYWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjMxLTI0OjQwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIlNOT01FRENUIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjEtMjU6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiRVIiLAogICAgICAgICAgICAiaWQiIDogIkVSIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkVtZXJnZW5jeSByb29tIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNToyMi0yNTozMSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJSb2xlQ29kZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoxLTI2OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIklDVSIsCiAgICAgICAgICAgICJpZCIgOiAiSUNVIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkludGVuc2l2ZSBjYXJlIHVuaXQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjI0LTI2OjMzIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIlJvbGVDb2RlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEtMjc6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiQmlsbGluZyIsCiAgICAgICAgICAgICJpZCIgOiAiYmlsbGluZyIsCiAgICAgICAgICAgICJkaXNwbGF5IiA6ICJCaWxsaW5nIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzozMi0yNzo0NyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDoxLTMwOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogImFjdGl2ZSIsCiAgICAgICAgICAgICJpZCIgOiAiYWN0aXZlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDozMC0zMDo1OSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEtMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAicmVjdXJyZW5jZSIsCiAgICAgICAgICAgICJpZCIgOiAicmVjdXJyZW5jZSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MzgtMzE6NjciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxLTMyOjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogInJlbGFwc2UiLAogICAgICAgICAgICAiaWQiIDogInJlbGFwc2UiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMyLTMyOjYxIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM6MS0zMzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpbmFjdGl2ZSIsCiAgICAgICAgICAgICJpZCIgOiAiaW5hY3RpdmUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjM0LTMzOjYzIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MS0zNDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZW1pc3Npb24iLAogICAgICAgICAgICAiaWQiIDogInJlbWlzc2lvbiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MzYtMzQ6NjUiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxLTM1OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmVkIiwKICAgICAgICAgICAgImlkIiA6ICJyZXNvbHZlZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzQtMzU6NjMiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxLTM4OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogInVuY29uZmlybWVkIiwKICAgICAgICAgICAgImlkIiA6ICJ1bmNvbmZpcm1lZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDAtMzg6NzEiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS0zOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJwcm92aXNpb25hbCIsCiAgICAgICAgICAgICJpZCIgOiAicHJvdmlzaW9uYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjQwLTM5OjcxIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjEtNDA6NzMiLAogICAgICAgICAgICAibmFtZSIgOiAiZGlmZmVyZW50aWFsIiwKICAgICAgICAgICAgImlkIiA6ICJkaWZmZXJlbnRpYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjQyLTQwOjczIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjEtNDE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiY29uZmlybWVkIiwKICAgICAgICAgICAgImlkIiA6ICJjb25maXJtZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjM2LTQxOjY3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjEtNDI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmdXRlZCIsCiAgICAgICAgICAgICJpZCIgOiAicmVmdXRlZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MzItNDI6NjMiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MS00Mzo4MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJlbnRlcmVkLWluLWVycm9yIiwKICAgICAgICAgICAgImlkIiA6ICJlbnRlcmVkLWluLWVycm9yIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo1MC00Mzo4MSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NjoxLTQ2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3ktYWN0aXZlIiwKICAgICAgICAgICAgImlkIiA6ICJhY3RpdmUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjM4LTQ2Ojc2IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZUNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MS00Nzo4MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5LWluYWN0aXZlIiwKICAgICAgICAgICAgImlkIiA6ICJpbmFjdGl2ZSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6NDItNDc6ODAiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTQ4OjgwIiwKICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3ktcmVzb2x2ZWQiLAogICAgICAgICAgICAiaWQiIDogInJlc29sdmVkIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo0Mi00ODo4MCIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEtNTE6ODgiLAogICAgICAgICAgICAibmFtZSIgOiAiYWxsZXJneS11bmNvbmZpcm1lZCIsCiAgICAgICAgICAgICJpZCIgOiAidW5jb25maXJtZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjQ4LTUxOjg4IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZVZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEtNTI6ODQiLAogICAgICAgICAgICAibmFtZSIgOiAiYWxsZXJneS1jb25maXJtZWQiLAogICAgICAgICAgICAiaWQiIDogImNvbmZpcm1lZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDQtNTI6ODQiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MS01Mzo4MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5LXJlZnV0ZWQiLAogICAgICAgICAgICAiaWQiIDogInJlZnV0ZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjQwLTUzOjgwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZVZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEtNTY6ODIiLAogICAgICAgICAgICAibmFtZSIgOiAiQ29tbXVuaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJjb21tdW5pdHkiLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiQ29tbXVuaXR5IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjozNi01Njo2MiIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5IgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjEtNTc6ODIiLAogICAgICAgICAgICAibmFtZSIgOiAiRGlzY2hhcmdlIiwKICAgICAgICAgICAgImlkIiA6ICJkaXNjaGFyZ2UiLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiRGlzY2hhcmdlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NzozNi01Nzo2MiIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5IgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjEtNjA6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiQUQiLAogICAgICAgICAgICAiaWQiIDogIkFEIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkFkbWlzc2lvbiBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjIyLTYwOjM3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjEtNjE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiREQiLAogICAgICAgICAgICAiaWQiIDogIkREIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkRpc2NoYXJnZSBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjIyLTYxOjM3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjEtNjI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiQ0MiLAogICAgICAgICAgICAiaWQiIDogIkNDIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkNoaWVmIGNvbXBsYWludCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6MjItNjI6MzciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MS02Mzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJDTSIsCiAgICAgICAgICAgICJpZCIgOiAiQ00iLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiQ29tb3JiaWRpdHkgZGlhZ25vc2lzIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MzoyMi02MzozNyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDoxLTY0OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogInByZS1vcCIsCiAgICAgICAgICAgICJpZCIgOiAicHJlLW9wIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogInByZS1vcCBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjMwLTY0OjQ1IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY1OjEtNjU6NzUiLAogICAgICAgICAgICAibmFtZSIgOiAicG9zdC1vcCIsCiAgICAgICAgICAgICJpZCIgOiAicG9zdC1vcCIsCiAgICAgICAgICAgICJkaXNwbGF5IiA6ICJwb3N0LW9wIGRpYWdub3NpcyIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MzItNjU6NDciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjY6MS02Njo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJiaWxsaW5nIiwKICAgICAgICAgICAgImlkIiA6ICJiaWxsaW5nIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogImJpbGxpbmcgZGlhZ25vc2lzIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NjozMi02Njo0NyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OToxLTk5OjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOb3JtYWxpemVzIGEgdmFsdWUgdGhhdCBpcyBhIGNob2ljZSBvZiB0aW1pbmctdmFsdWVkIHR5cGVzIHRvIGFuIGVxdWl2YWxlbnQgaW50ZXJ2YWwiCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5vcm1hbGl6ZXMgYSBjaG9pY2UgdHlwZSBvZiBGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFuY2UsIEZISVIuc3RyaW5nLCBGSElSLkFnZSwgb3IgRkhJUi5SYW5nZSB0eXBlc1xudG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlXG5yZXByZXNlbnRhdGlvbnMgZm9yIHRpbWluZy12YWx1ZWQgZWxlbWVudHMgaW4gRkhJUiwgYWxsb3dpbmcgdGhpcyBmdW5jdGlvbiB0byBiZSB1c2VkIGFjcm9zcyBhbnkgcmVzb3VyY2UuIE5PVEU6IER1ZSB0byB0aGVcbmNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYSBzaW5nbGUgaW50ZXJ2YWwgZnJvbSBhIFRpbWluZyBvciBTdHJpbmcgdHlwZSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIGl0IGlzIHVzZWRcbndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjMtOTk6NCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYXNlIiwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6NC04MjoxMTEiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTo5LTgxOjMxIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6OS04MToxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MToxOS04MTozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6Ni04MjoxMTEiLAogICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MTUtODI6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjM4LTgyOjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MzgtODI6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6NDgtODI6NjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo2NC04MjoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjg3LTgyOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjg3LTgyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjk3LTgyOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MzozLTg0OjQ5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6OC04MzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjgtODM6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MTgtODM6MjgiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDo1LTg0OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDoyOC04NDo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjI4LTg0OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjM4LTg0OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6My04NjoxMDciLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NTo4LTg1OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6OC04NToxMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NToxOC04NToyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0LTg2OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxMy04Njo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6MzYtODY6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjozNi04Njo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0Ni04Njo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6NjEtODY6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo4NC04NjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo4NC04Njo4OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo5NC04NjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW5zdGFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzozLTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MTgtODc6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NS04OTo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MTQtODg6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODozMy04ODo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NTQtODg6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojc3LTg4Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6ODIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6ODctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5Ojg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OToyNS04OTo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NDYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjY5LTg5Ojg2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6NzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NzktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjkxLTg5Ojk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZEV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93Q2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1LTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4OjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1NC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo4MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo4Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo0Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo3NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTEtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjUtODk6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4Ojk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MTQtODg6NTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODozMy04ODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjU0LTg4Ojk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojc3LTg4OjgyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojg3LTg4Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5Ojk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6NDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OToyNS04OTozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjQ2LTg5Ojg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjY5LTg5Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5Ojc5LTg5Ojg2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo5MS04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1LTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4OjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1NC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo4MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo4Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo0Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo3NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTEtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjMtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6OC05MDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjgtOTA6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6MTgtOTA6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1LTkyOjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MTQtOTE6NTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzMtOTE6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjU0LTkxOjEwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzctOTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzctOTE6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3OC05MTo4MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo4OC05MTo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6NDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MjUtOTI6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05MjozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjQ2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2OS05Mjo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2OS05Mjo5MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjcwLTkyOjc1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjgwLTkyOjg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MTAwLTkyOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NS05MjoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjE0LTkxOjEwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjE0LTkxOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzMtOTE6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1NC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc3LTkxOjEwMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc3LTkxOjk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzgtOTE6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6ODgtOTE6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MjUtOTI6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo0Ni05Mjo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NjktOTI6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NjktOTI6OTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo3MC05Mjo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo4MC05Mjo4OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjEwMC05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjUtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MTo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc4LTkxOjgzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjg4LTkxOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05Mjo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjkwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NzAtOTI6NzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6ODAtOTI6ODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxMDAtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2hDbG9zZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjUtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MTo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc4LTkxOjgzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjg4LTkxOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05Mjo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjkwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NzAtOTI6NzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6ODAtOTI6ODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxMDAtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo1LTk0OjExNCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjEwLTkzOjMwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6MTAtOTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6MjAtOTM6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDo1LTk0OjExNCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDoxMy05NDozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjEzLTk0OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MjEtOTQ6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDozMC05NDozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NDEtOTQ6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NDctOTQ6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NTItOTQ6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NjEtOTQ6MTEzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkNhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6NS05NjoxMTEiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxMC05NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEwLTk1OjE1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjIwLTk1OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6Ny05NjoxMTEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MTUtOTY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NjoxNS05NjoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjIzLTk2OjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MzItOTY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjQzLTk2OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjQ5LTk2OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjU0LTk2OjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjYzLTk2OjExMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJDYW5ub3QgY29tcHV0ZSBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nIHZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjctOTg6MzIiLAogICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODo3LTk4OjEwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6MTUtOTg6MzIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoyNC05ODozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6MzUtNzk6MTMwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNob2ljZVR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAiY2hvaWNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6NDItNzk6NTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo1Ny03OTo2NyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo3MC03OTo4MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UaW1pbmciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo4My03OTo5NCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6OTctNzk6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjExMC03OToxMTciLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6MTIwLTc5OjEyOSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDc6MS0xMDk6MjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbEZyb21MaXN0IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYSBsaXN0IG9mIGludGVydmFscyByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgRXZlbnQgb3IgQm91bmRzIG9mIGEgVGltaW5nIHJlc291cmNlLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogVGltaW5nLkJvdW5kcyBpcyByZXN0cmljdGVkIHRvIGEgUGVyaW9kLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjMtMTA5OjI0IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODozLTEwODo5IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJ2IiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjMtMTA4OjciLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1LTEwOToyNCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOToxMi0xMDk6MjQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDaG9pY2VUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNob2ljZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW5zdGFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo0Mi0xMDc6NjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGlzdFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjQ3LTEwNzo1OSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjEtMTEyOjQyIiwKICAgICAgICAgICAgIm5hbWUiIDogIk51bGxUb0VtcHR5SW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjMtMTEyOjQyIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6Ni0xMTI6MjIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjYtMTEyOjIyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMjo2LTExMjoxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjI5LTExMjozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6NDAtMTEyOjQyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NDMtMTExOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3RUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMTo0OC0xMTE6NjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NTctMTExOjY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjEtMTE4OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvVGltaW5nSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjMtMTE4OjQ3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkV4Y2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjMtMTE4OjMxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjExLTExODozIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlVuaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6NS0xMTY6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTnVsbFRvRW1wdHlJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjI1LTExNjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsRnJvbUxpc3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjo0NC0xMTY6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZXZlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjo0NC0xMTY6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MTEtMTE3OjgwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk51bGxUb0VtcHR5SW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzozMS0xMTc6NzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGlzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjMyLTExNzo3OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo0My0xMTc6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6NDMtMTE3OjYyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJvdW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQzLTExNzo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJyZXBlYXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo0My0xMTc6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo2Ny0xMTc6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNob2ljZVR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hvaWNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UaW1pbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9c3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAiZGlzdGluY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiWCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE4OjQwLTExODo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDItMTE4OjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJkaXN0aW5jdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDo0MS0xMTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTU0OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQWJhdGVtZW50SW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAicmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmVcbk5PVEU6IEZ1bmN0aW9uIG92ZXJsb2FkcyBpbmNvbnNpc3RlbnRseSBlcnIgaW4gQ1FMIDEuNSwgdGhpcyBpcyBjb21tZW50ZWQgdW50aWwgYSBmaXggaXMgZm91bmRcbmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKG9ic2VydmF0aW9uIEZISVIuT2JzZXJ2YXRpb24pOlxuICBvYnNlcnZhdGlvbiBvXG4gICAgcmV0dXJuIENvYWxlc2NlKFRvSW50ZXJ2YWwoby5lZmZlY3RpdmUpLCBUb0ludGVydmFsKG8uaXNzdWVkKSkiCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93XG5hIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiB1c2VkIHdpdGggYSBDb25kaXRpb24gaW5zdGFuY2UgdGhhdCBoYXMgYSBTdHJpbmcgYXMgdGhlIGFiYXRlbWVudCB2YWx1ZS4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjozLTE1NDoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM4OjQ0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM2OjY0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM2OjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo2LTEzNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6Ni0xMzY6MjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAib25zZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo2LTEzNjoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjM4LTEzNjo2NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6MzgtMTM2OjU2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjM4LTEzNjo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzAtMTM4OjQ0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVpdmFsZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNsaW5pY2FsU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2Ojc5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6OTgtMTM2OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJhY3RpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjgtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWl2YWxlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNzo4LTEzNzozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjbGluaWNhbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjgtMTM3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MzUtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlY3VycmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo4LTEzODo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVpdmFsZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzg6OC0xMzg6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY2xpbmljYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo4LTEzODoxNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM4OjM1LTEzODo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWxhcHNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvZGVSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjUtMTM5OjI2IiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxNC0xMzk6MTgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm93IgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzk6MjEtMTM5OjI1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdyIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6OC0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxMS0xNDA6NDYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6MTEtMTQwOjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjExLTE0MDoxOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNC0xNDA6NDYiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo0LTE0MToxMzUiLAogICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEzLTE0MTo3MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjM2LTE0MTo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTozNi0xNDE6NTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6MzYtMTQxOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjU5LTE0MTo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo3NS0xNDE6MTM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6OTgtMTQxOjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo5OC0xNDE6MTE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjk4LTE0MToxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6MTIxLTE0MToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo3LTE1NDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEwLTE0Mjo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MjoxMC0xNDI6MjgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MTAtMTQyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjMzLTE0Mjo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mzo0LTE0Mzo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjI3LTE0Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoyNy0xNDM6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6MjctMTQzOjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6Ny0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NDoxMC0xNDQ6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MTAtMTQ0OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEwLTE0NDoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NDozMy0xNDQ6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9c3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NS0xNDU6MTA5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NToxMy0xNDU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MTMtMTQ1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjIxLTE0NTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTozMC0xNDU6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTo0MS0xNDU6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjQ3LTE0NTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZXZlcml0eSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTItMTQ1OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTo2MS0xNDU6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkNhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6Ny0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxMC0xNDY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MTAtMTQ2OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ2OjEwLTE0NjoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjozMy0xNDY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6My0xNDg6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzoxMi0xNDc6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMxLTE0Nzo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6NTItMTQ3OjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0NzoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6NzUtMTQ3OjkzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo4MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo5OC0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NC0xNDg6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4Ojk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoyMy0xNDg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQ0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojc1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjkwLTE0ODo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjEwMi0xNDg6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MTAtMTQ5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjEwLTE0OToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0OToxMC0xNDk6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MzMtMTQ5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NC0xNTE6MTE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjExNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMy0xNTA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjMyLTE1MDo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTMtMTUwOjExNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzYtMTUwOjExMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ny0xNTA6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEwMC0xNTA6MTA5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjI0LTE1MTo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NDUtMTUxOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY4LTE1MToxMDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY4LTE1MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NjktMTUxOjg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo3NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo5Mi0xNTE6MTAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjExMi0xNTE6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTI6MTAtMTUyOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEwLTE1MjoyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjoxMC0xNTI6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTI6MzMtMTUyOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mzo0LTE1Mzo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjEzLTE1Mzo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MjAtMTUzOjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzozMS0xNTM6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAib25zZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzozMS0xNTM6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mzo0OS0xNTM6NzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicmVjb3JkZWREYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NDktMTUzOjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ3LTEzNTo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MS0xNjI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9QcmV2YWxlbmNlSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbVxub25zZXQgdG8gYWJhdGVtZW50IGFzIHNwZWNpZmllZCBpbiB0aGUgZ2l2ZW4gQ29uZGl0aW9uLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjMtMTYyOjg3IiwKICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MTItMTYyOjQ3IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjIxLTE2Mjo0NyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MzItMTYyOjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogIm9uc2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MzItMTYyOjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo1MC0xNjI6ODYiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjU3LTE2Mjo4NiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0FiYXRlbWVudEludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6NzctMTYyOjg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MTo0OC0xNjE6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY4OjEtMTY5OjEwOSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1ByZXZhbGVuY2VJbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBwcmV2YWxlbmNlIHBlcmlvZCBvZiBhIGdpdmVuIEFsbGVyZ3kgSW50b2xlcmFuY2UgcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJVc2VzIHRoZSBUb0ludGVydmFsIGZ1bmN0aW9uIGFuZCBsYXN0T2NjdXJlbmNlIHRvIGRldGVybWluZSB0aGUgd2lkZXN0IHBvdGVudGlhbCBpbnRlcnZhbC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozLTE2OToxMDkiLAogICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEyLTE2OTo1NiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OToyMS0xNjk6NTYiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjMyLTE2OTo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJvbnNldCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjMyLTE2OTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5SW50b2xlcmFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6NTktMTY5OjEwOCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo2OC0xNjk6MTAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxhc3RPY2N1cnJlbmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6NjgtMTY5Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6MTAzLTE2OToxMDciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm93IgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NTctMTY4Ojc0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE4MToxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZVxuQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6My0xODE6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMtMTc5OjI4IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMtMTc5OjI2IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NC0xODA6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTgwOjEwLTE4MDoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MTgtMTgwOjIwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6My0xODE6MTAiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6MTAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc4OjQzLTE3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODo2My0xNzg6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6MS0xODk6NTAiLAogICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZVxuaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6My0xODk6NTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OToxOC0xODk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjMxLTE4OTo0NCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6NDctMTg5OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo0Mi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NjItMTg4OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjEtMTk5OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIkV4dGVuc2lvbnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbnkgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzc1xuY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6My0xOTk6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjMtMTk3OjIxIiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjMtMTk3OjE5IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODo0LTE5ODoyMCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MTAtMTk4OjE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxOC0xOTg6MjAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozLTE5OToxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OToxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NjozNi0xOTY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Njo0OS0xOTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDc6NDEiLAogICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzozLTIwNzo0MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA3OjE4LTIwNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFeHRlbnNpb25zIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MjktMjA3OjM1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA3OjM4LTIwNzo0MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNjozNS0yMDY6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNjo0OC0yMDY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MS0yMTg6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZVxuQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6My0yMTg6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjMtMjE2OjM2IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjMtMjE2OjM0IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogIm1vZGlmaWVyRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0LTIxNzoyMCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTc6MTAtMjE3OjE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzoxOC0yMTc6MjAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODozLTIxODoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NTEtMjE1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE1OjcxLTIxNTo3NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNToxLTIyNjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJNb2RpZmllckV4dGVuc2lvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIHRoZSBzaW5nbGUgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjMtMjI2OjU2IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MTgtMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjM3LTIyNjo1MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTMtMjI2OjU1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1MC0yMjU6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6NzAtMjI1Ojc1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjEtMjM2OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDozLTIzNjoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6My0yMzQ6MjkiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6My0yMzQ6MjciLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NC0yMzU6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM1OjEwLTIzNToxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MTgtMjM1OjIwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6My0yMzY6MTAiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6MTAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NDQtMjMzOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJhY2tib25lRWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMzo2NS0yMzM6NzAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6MS0yNDQ6NDkiLAogICAgICAgICAgICAibmFtZSIgOiAiTW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjMtMjQ0OjQ5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDQ6MTgtMjQ0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjM3LTI0NDo0MyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0Ni0yNDQ6NDgiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6NDMtMjQzOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJhY2tib25lRWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo2NC0yNDM6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MS0yNTQ6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZUV4dGVuc2lvbnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3NcbmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUyOjMtMjU0OjEwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjozLTI1MjoyOCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjozLTI1MjoyNiIsCiAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjQtMjUzOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzoxMC0yNTM6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjE4LTI1Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjE5LTI1Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo2NC0yNTM6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDozLTI1NDoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NDctMjUxOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NjYtMjUxOjcxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjEtMjYyOjUxIiwKICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VFeHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyB0aGUgc2luZ2xlIGJhc2UtRkhJUiBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozLTI2Mjo1MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjE4LTI2Mjo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjMzLTI2Mjo0NiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjUwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjQ2LTI2MTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjY1LTI2MTo3MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI3MjoxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDozLTI3MjoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6My0yNzA6MjEiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6My0yNzA6MTkiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjQtMjcxOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MToxMC0yNzE6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjE4LTI3MTo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjE5LTI3MTo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo2NC0yNzE6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozLTI3MjoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo0MC0yNjk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY5OjUyLTI2OTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OToxLTI4MDo0NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDozLTI4MDo0NCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgwOjE4LTI4MDo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgwOjMzLTI4MDozOSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDo0Mi0yODA6NDMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozOS0yNzk6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjUxLTI3OTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzoxLTI5MDoxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4ODozLTI5MDoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6My0yODg6MzYiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6My0yODg6MzQiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjQtMjg5OjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OToxMC0yODk6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjE4LTI4OTo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjE5LTI4OTo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo2NC0yODk6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDozLTI5MDoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6NTUtMjg3OjY4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6NzQtMjg3Ojc5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk3OjEtMjk4OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VNb2RpZmllckV4dGVuc2lvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMtMjk4OjU5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MTgtMjk4OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VNb2RpZmllckV4dGVuc2lvbnMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODo0MS0yOTg6NTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjU3LTI5ODo1OCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo1NC0yOTc6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo3My0yOTc6NzgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDU6MS0zMDg6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozLTMwODoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6My0zMDY6MjkiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6My0zMDY6MjciLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6NC0zMDc6NjYiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjEwLTMwNzoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MTgtMzA3OjY2IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MTktMzA3OjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi8iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjY0LTMwNzo2NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMtMzA4OjEwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjEwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQ4LTMwNTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CYWNrYm9uZUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo2OC0zMDU6NzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTY6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNjozLTMxNjo1MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjE4LTMxNjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6NDEtMzE2OjQ3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjUwLTMxNjo1MSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CYWNrYm9uZUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNTo2Ny0zMTU6NzIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/FHIRCommon" - } - }, - { - "resource": { - "resourceType": "Patient", - "id": "CMSTest-patient-1", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2028-9", - "display": "Asian" - } - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2135-2", - "display": "Hispanic or Latino" - } - } - ] - } - ], - "identifier": [ - { - "use": "usual", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MR", - "display": "Medical Record Number" - } - ] - }, - "system": "http://hospital.smarthealthit.org", - "value": "999999992" - } - ], - "name": [ - { - "family": "Dere", - "given": [ - "Ben" - ] - } - ], - "gender": "male", - "birthDate": "1965-01-01" - }, - "request": { - "method": "PUT", - "url": "Patient/CMSTest-patient-1" - } - } - ] + "resourceType": "Bundle", + "id": "CMSTest-bundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.837", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.837" + } + ], + "version": "20121025", + "name": "Ethnicity", + "title": "Ethnicity", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Ethnicity.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2135-2", + "display": "Hispanic or Latino" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2186-5", + "display": "Not Hispanic or Latino" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.837" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.3591", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.3591" + } + ], + "version": "20180718", + "name": "Payer", + "title": "Payer", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Payer.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "1", + "display": "MEDICARE" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "11", + "display": "Medicare Managed Care (Includes Medicare Advantage Plans)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "111", + "display": "Medicare HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "112", + "display": "Medicare PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "113", + "display": "Medicare POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "119", + "display": "Medicare Managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "12", + "display": "Medicare (Non-managed Care)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "121", + "display": "Medicare FFS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "122", + "display": "Medicare Drug Benefit" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "123", + "display": "Medicare Medical Savings Account (MSA)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "129", + "display": "Medicare Non-managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "13", + "display": "Medicare Hospice" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "14", + "display": "Dual Eligibility Medicare/Medicaid Organization" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "19", + "display": "Medicare Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "191", + "display": "Medicare Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "2", + "display": "MEDICAID" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "21", + "display": "Medicaid (Managed Care)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "211", + "display": "Medicaid HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "212", + "display": "Medicaid PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "213", + "display": "Medicaid PCCM (Primary Care Case Management)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "219", + "display": "Medicaid Managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "22", + "display": "Medicaid (Non-managed Care Plan)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "23", + "display": "Medicaid/SCHIP" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "25", + "display": "Medicaid - Out of State" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "26", + "display": "Medicaid - Long Term Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "29", + "display": "Medicaid Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "291", + "display": "Medicaid Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "299", + "display": "Medicaid - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3", + "display": "OTHER GOVERNMENT (Federal/State/Local) (excluding Department of Corrections)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "31", + "display": "Department of Defense" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "311", + "display": "TRICARE (CHAMPUS)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3111", + "display": "TRICARE Prime--HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3112", + "display": "TRICARE Extra--PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3113", + "display": "TRICARE Standard - Fee For Service" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3114", + "display": "TRICARE For Life--Medicare Supplement" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3115", + "display": "TRICARE Reserve Select" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3116", + "display": "Uniformed Services Family Health Plan (USFHP) -- HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3119", + "display": "Department of Defense - (other)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "312", + "display": "Military Treatment Facility" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3121", + "display": "Enrolled Prime--HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3122", + "display": "Non-enrolled Space Available" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3123", + "display": "TRICARE For Life (TFL)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "313", + "display": "Dental --Stand Alone" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32", + "display": "Department of Veterans Affairs" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "321", + "display": "Veteran care-Care provided to Veterans" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3211", + "display": "Direct Care-Care provided in VA facilities" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3212", + "display": "Indirect Care-Care provided outside VA facilities" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32121", + "display": "Fee Basis" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32122", + "display": "Foreign Fee/Foreign Medical Program (FMP)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32123", + "display": "Contract Nursing Home/Community Nursing Home" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32124", + "display": "State Veterans Home" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32125", + "display": "Sharing Agreements" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32126", + "display": "Other Federal Agency" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32127", + "display": "Dental Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32128", + "display": "Vision Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "322", + "display": "Non-veteran care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3221", + "display": "Civilian Health and Medical Program for the VA (CHAMPVA)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3222", + "display": "Spina Bifida Health Care Program (SB)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3223", + "display": "Children of Women Vietnam Veterans (CWVV)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3229", + "display": "Other non-veteran care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "33", + "display": "Indian Health Service or Tribe" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "331", + "display": "Indian Health Service - Regular" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "332", + "display": "Indian Health Service - Contract" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "333", + "display": "Indian Health Service - Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "334", + "display": "Indian Tribe - Sponsored Coverage" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "34", + "display": "HRSA Program" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "341", + "display": "Title V (MCH Block Grant)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "342", + "display": "Migrant Health Program" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "343", + "display": "Ryan White Act" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "349", + "display": "Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "35", + "display": "Black Lung" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "36", + "display": "State Government" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "361", + "display": "State SCHIP program (codes for individual states)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "362", + "display": "Specific state programs (list/ local code)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "369", + "display": "State, not otherwise specified (other state)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "37", + "display": "Local Government" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "371", + "display": "Local - Managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3711", + "display": "HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3712", + "display": "PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3713", + "display": "POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "372", + "display": "FFS/Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "379", + "display": "Local, not otherwise specified (other local, county)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "38", + "display": "Other Government (Federal, State, Local not specified)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "381", + "display": "Federal, State, Local not specified managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3811", + "display": "Federal, State, Local not specified - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3812", + "display": "Federal, State, Local not specified - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3813", + "display": "Federal, State, Local not specified - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3819", + "display": "Federal, State, Local not specified - not specified managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "382", + "display": "Federal, State, Local not specified - FFS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "389", + "display": "Federal, State, Local not specified - Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "39", + "display": "Other Federal" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "391", + "display": "Federal Employee Health Plan - Use when known" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "4", + "display": "DEPARTMENTS OF CORRECTIONS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "41", + "display": "Corrections Federal" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "42", + "display": "Corrections State" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "43", + "display": "Corrections Local" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "44", + "display": "Corrections Unknown Level" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "5", + "display": "PRIVATE HEALTH INSURANCE" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "51", + "display": "Managed Care (Private)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "511", + "display": "Commercial Managed Care - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "512", + "display": "Commercial Managed Care - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "513", + "display": "Commercial Managed Care - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "514", + "display": "Exclusive Provider Organization" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "515", + "display": "Gatekeeper PPO (GPPO)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "516", + "display": "Commercial Managed Care - Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "517", + "display": "Commercial Managed Care - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "519", + "display": "Managed Care, Other (non HMO)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "52", + "display": "Private Health Insurance - Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "521", + "display": "Commercial Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "522", + "display": "Self-insured (ERISA) Administrative Services Only (ASO) plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "523", + "display": "Medicare supplemental policy (as second payer)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "524", + "display": "Indemnity Insurance - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "529", + "display": "Private health insurance--other commercial Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "53", + "display": "Managed Care (private) or private health insurance (indemnity), not otherwise specified" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "54", + "display": "Organized Delivery System" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "55", + "display": "Small Employer Purchasing Group" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "56", + "display": "Specialized Stand-Alone Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "561", + "display": "Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "562", + "display": "Vision" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "59", + "display": "Other Private Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "6", + "display": "BLUE CROSS/BLUE SHIELD" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "61", + "display": "BC Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "611", + "display": "BC Managed Care - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "612", + "display": "BC Managed Care - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "613", + "display": "BC Managed Care - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "614", + "display": "BC Managed Care - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "619", + "display": "BC Managed Care - Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "62", + "display": "BC Insurance Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "621", + "display": "BC Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "622", + "display": "BC Self-insured (ERISA) Administrative Services Only (ASO)Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "623", + "display": "BC Medicare Supplemental Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "629", + "display": "BC Indemnity - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "7", + "display": "MANAGED CARE, UNSPECIFIED (to be used only if one can't distinguish public from private)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "71", + "display": "HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "72", + "display": "PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "73", + "display": "POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "79", + "display": "Other Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "8", + "display": "NO PAYMENT from an Organization/Agency/Program/Private Payer Listed" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "81", + "display": "Self-pay (Includes applicants for insurance and Medicaid applicants)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "82", + "display": "No Charge" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "821", + "display": "Charity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "822", + "display": "Professional Courtesy" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "823", + "display": "Research/Clinical Trial" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "83", + "display": "Refusal to Pay/Bad Debt" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "84", + "display": "Hill Burton Free Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "85", + "display": "Research/Donor" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "89", + "display": "No Payment, Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "9", + "display": "MISCELLANEOUS/OTHER" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "91", + "display": "Foreign National" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "92", + "display": "Other (Non-government)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "93", + "display": "Disability Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "94", + "display": "Long-term Care Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "95", + "display": "Worker's Compensation" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "951", + "display": "Worker's Comp HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "953", + "display": "Worker's Comp Fee-for-Service" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "954", + "display": "Worker's Comp Other Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "959", + "display": "Worker's Comp, Other unspecified" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "96", + "display": "Auto Insurance (includes no fault)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "97", + "display": "Legal Liability / Liability Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "98", + "display": "Other specified but not otherwise classifiable (includes Hospice - Unspecified plan)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "99", + "display": "No Typology Code available for payment source" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "9999", + "display": "Unavailable / No Payer Specified / Blank" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.3591" + } + }, + { + "resource": { + "resourceType": "Condition", + "id": "CMSTest-patient-1-condition-1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", + "version": "0.5.0", + "code": "active", + "display": "Active" + } + ] + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", + "version": "0.5.0", + "code": "confirmed", + "display": "Confirmed" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/ValueSet/condition-category", + "version": "0.5.0", + "code": "encounter-diagnosis", + "display": "Encounter Diagnosis" + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2021", + "code": "O30.833", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" + } + ] + }, + "subject": { + "reference": "Patient/CMSTest-patient-1" + }, + "onsetDateTime": "2023-01-05T10:00:00-07:00" + }, + "request": { + "method": "PUT", + "url": "Condition/CMSTest-patient-1-condition-1" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.836", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.836" + } + ], + "version": "20121025", + "name": "Race", + "title": "Race", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Race.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "1002-5", + "display": "American Indian or Alaska Native" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2028-9", + "display": "Asian" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2054-5", + "display": "Black or African American" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2076-8", + "display": "Native Hawaiian or Other Pacific Islander" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2106-3", + "display": "White" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2131-1", + "display": "Other Race" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.836" + } + }, + { + "resource": { + "resourceType": "Measure", + "id": "CMSTest", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" + ] + }, + "contained": [ + { + "resourceType": "Library", + "id": "effective-data-requirements", + "status": "active", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "module-definition" + } + ] + }, + "relatedArtifact": [ + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + } + ] + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + }, + { + "id": "effective-data-requirements", + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements", + "valueReference": { + "reference": "#effective-data-requirements" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Measure/CMSTest", + "name": "CMSTest", + "title": "Measure - CMSTest: test", + "status": "active", + "experimental": true, + "date": "2023-06-21T16:16:00-07:00", + "publisher": "Alphora", + "contact": [ + { + "telecom": [ + { + "system": "url", + "value": "https://alphora.com" + } + ] + } + ], + "description": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "version": "4.0.1", + "code": "program", + "display": "Program" + }, + "valueCodeableConcept": { + "text": "eligible-provider" + } + } + ], + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ] + } + ], + "purpose": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", + "effectivePeriod": { + "start": "2020-01-01T00:00:00-07:00", + "end": "2020-12-31T23:59:59-07:00" + }, + "topic": [ + { + "coding": [ + { + "system": "http://loinc.org", + "code": "57024-2", + "display": "Health Quality Measure Document" + } + ] + } + ], + "library": [ + "http://content.alphora.com/fhir/dqm/Library/CMSTest" + ], + "scoring": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", + "version": "4.0.1", + "code": "proportion", + "display": "Proportion" + } + ] + }, + "type": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-type", + "version": "4.2.0", + "code": "process", + "display": "Process" + } + ] + } + ], + "rationale": "test.", + "clinicalRecommendationStatement": "test", + "improvementNotation": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", + "version": "0.1.0", + "code": "increase", + "display": "Increased score indicates improvement" + } + ] + }, + "group": [ + { + "id": "group-1", + "population": [ + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Initial Population" + } + }, + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator", + "display": "Denominator" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Denominator" + } + }, + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator", + "display": "Numerator" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Numerator" + } + } + ] + } + ], + "supplementalData": [ + { + "id": "sde-sex", + "code": { + "text": "sde-sex" + }, + "usage": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-data-usage", + "code": "supplemental-data" + } + ] + } + ], + "criteria": { + "language": "text/cql.identifier", + "expression": "SDE Sex" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "Measure/CMSTest" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "CMSTest", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/CMSTest", + "name": "CMSTest", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FC", + "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon|4.0.1" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Code system ConditionClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Value set Pregnancy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Pregnant Patient", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDTVNUZXN0Cgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkhJUkhlbHBlcnMKaW5jbHVkZSBGSElSQ29tbW9uIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkMKCmluY2x1ZGUgU0RFIGNhbGxlZCBTREUKdmFsdWVzZXQgIlByZWduYW5jeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMy0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIzLTEyLTMxVDAwOjAwOjAwLjAwMCkKCmNvbnRleHQgUGF0aWVudAoKZGVmaW5lICJTREUgU2V4IjoKICBTREUuIlNERSBTZXgiCgpkZWZpbmUgIkluaXRpYWwgUG9wdWxhdGlvbiI6CiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA+IDE4CiAgICAgICAgICAgIApkZWZpbmUgZnVuY3Rpb24gUXVhbGlmaWVkQ29uZGl0aW9ucyh2YWx1ZSBMaXN0PEZISVIuQ29uZGl0aW9uPik6CiAgdmFsdWUgQ29uZGl0aW9uCiAgICB3aGVyZSAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24uY2xpbmljYWxTdGF0dXMpIH4gRkMuImFjdGl2ZSIKICAgICkKICAgIGFuZCAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24udmVyaWZpY2F0aW9uU3RhdHVzKSB+IEZDLiJjb25maXJtZWQiCiAgICApCgpkZWZpbmUgIkRlbm9taW5hdG9yIjoKICAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA8IDk5CgoKZGVmaW5lICJOdW1lcmF0b3IiOgogIGV4aXN0cyAiUHJlZ25hbnQgUGF0aWVudCIKCmRlZmluZSAiUHJlZ25hbnQgUGF0aWVudCI6ICAgCiAgKFF1YWxpZmllZENvbmRpdGlvbnMoW0NvbmRpdGlvbjogIlByZWduYW5jeSJdKSkgUHJlZ25hbmN5CiB3aGVyZSBGQy5Ub1ByZXZhbGVuY2VJbnRlcnZhbChQcmVnbmFuY3kpIG92ZXJsYXBzICJNZWFzdXJlbWVudCBQZXJpb2QiICAgICAgICAgCiAgCgoKCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/CMSTest" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "FHIRHelpers", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers", + "version": "4.0.1", + "name": "FHIRHelpers", + "title": "Library - FHIR Helpers", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "Logic library used in mapping CQL logic with the R4 FHIR Data Model.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBUaGlzIGxpYnJhcnkgZGVmaW5lcyBmdW5jdGlvbnMgdG8gY29udmVydCBiZXR3ZWVuIEZISVIKIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydAogRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZToKIGh0dHBzOi8vZ2l0aHViLmNvbS9jcWZyYW1ld29yay9jbGluaWNhbF9xdWFsaXR5X2xhbmd1YWdlL3dpa2kvRkhJUkhlbHBlcnMKQGFsbG93Rmx1ZW50OiB0cnVlCiovCmxpYnJhcnkgRkhJUkhlbHBlcnMgdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocGVyaW9kIEZISVIuUGVyaW9kKToKICAgIGlmIHBlcmlvZCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgaWYgcGVyaW9kLiJzdGFydCIgaXMgbnVsbCB0aGVuCiAgICAgICAgICAgIEludGVydmFsKHBlcmlvZC4ic3RhcnQiLnZhbHVlLCBwZXJpb2QuImVuZCIudmFsdWVdCiAgICAgICAgZWxzZQogICAgICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ2FsZW5kYXJVbml0KHVuaXQgU3lzdGVtLlN0cmluZyk6CiAgICBjYXNlIHVuaXQKICAgICAgICB3aGVuICdtcycgdGhlbiAnbWlsbGlzZWNvbmQnCiAgICAgICAgd2hlbiAncycgdGhlbiAnc2Vjb25kJwogICAgICAgIHdoZW4gJ21pbicgdGhlbiAnbWludXRlJwogICAgICAgIHdoZW4gJ2gnIHRoZW4gJ2hvdXInCiAgICAgICAgd2hlbiAnZCcgdGhlbiAnZGF5JwogICAgICAgIHdoZW4gJ3drJyB0aGVuICd3ZWVrJwogICAgICAgIHdoZW4gJ21vJyB0aGVuICdtb250aCcKICAgICAgICB3aGVuICdhJyB0aGVuICd5ZWFyJwogICAgICAgIGVsc2UgdW5pdAogICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9RdWFudGl0eShxdWFudGl0eSBGSElSLlF1YW50aXR5KToKICAgIGNhc2UKICAgICAgICB3aGVuIHF1YW50aXR5IGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS52YWx1ZSBpcyBudWxsIHRoZW4gbnVsbAogICAgICAgIHdoZW4gcXVhbnRpdHkuY29tcGFyYXRvciBpcyBub3QgbnVsbCB0aGVuCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuQ29tcGFyYXRvclF1YW50aXR5Tm90U3VwcG9ydGVkJywgJ0Vycm9yJywgJ0ZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4nKQogICAgICAgIHdoZW4gcXVhbnRpdHkuc3lzdGVtIGlzIG51bGwgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly91bml0c29mbWVhc3VyZS5vcmcnCiAgICAgICAgICAgICAgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMnIHRoZW4KICAgICAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBUb0NhbGVuZGFyVW5pdChDb2FsZXNjZShxdWFudGl0eS5jb2RlLnZhbHVlLCBxdWFudGl0eS51bml0LnZhbHVlLCAnMScpKSB9CiAgICAgICAgZWxzZQogICAgICAgICAgICBNZXNzYWdlKG51bGwsIHRydWUsICdGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHknLCAnRXJyb3InLCAnSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICcgJiBxdWFudGl0eS51bml0LnZhbHVlICYgJyAoJyAmIHF1YW50aXR5LnN5c3RlbS52YWx1ZSAmICd8JyAmIHF1YW50aXR5LmNvZGUudmFsdWUgJiAnKScpCiAgICBlbmQKCmRlZmluZSBmdW5jdGlvbiBUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yKHF1YW50aXR5IEZISVIuUXVhbnRpdHkpOgogICAgY2FzZQogICAgICAgIHdoZW4gcXVhbnRpdHkgaXMgbnVsbCB0aGVuIG51bGwKICAgICAgICB3aGVuIHF1YW50aXR5LnZhbHVlIGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS5zeXN0ZW0gaXMgbnVsbCBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZycKICAgICAgICAgICAgICBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cycgdGhlbgogICAgICAgICAgICBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogcXVhbnRpdHkudmFsdWUudmFsdWUsIHVuaXQ6IFRvQ2FsZW5kYXJVbml0KENvYWxlc2NlKHF1YW50aXR5LmNvZGUudmFsdWUsIHF1YW50aXR5LnVuaXQudmFsdWUsICcxJykpIH0KICAgICAgICBlbHNlCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eScsICdFcnJvcicsICdJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogJyAmIHF1YW50aXR5LnVuaXQudmFsdWUgJiAnICgnICYgcXVhbnRpdHkuc3lzdGVtLnZhbHVlICYgJ3wnICYgcXVhbnRpdHkuY29kZS52YWx1ZSAmICcpJykKICAgIGVuZAoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4gbnVsbCBlbHNlCiAgICAgICAgY2FzZSBxdWFudGl0eS5jb21wYXJhdG9yLnZhbHVlCiAgICAgICAgICAgIHdoZW4gJzwnIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgd2hlbiAnPD0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPj0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIFRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IocXVhbnRpdHkpLAogICAgICAgICAgICAgICAgICAgIG51bGwKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPicgdGhlbgogICAgICAgICAgICAgICAgSW50ZXJ2YWwoCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSksCiAgICAgICAgICAgICAgICAgICAgbnVsbAogICAgICAgICAgICAgICAgXQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBJbnRlcnZhbFtUb1F1YW50aXR5KHF1YW50aXR5KSwgVG9RdWFudGl0eShxdWFudGl0eSldCiAgICAgICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9SYXRpbyhyYXRpbyBGSElSLlJhdGlvKToKICAgIGlmIHJhdGlvIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uUmF0aW8geyBudW1lcmF0b3I6IFRvUXVhbnRpdHkocmF0aW8ubnVtZXJhdG9yKSwgZGVub21pbmF0b3I6IFRvUXVhbnRpdHkocmF0aW8uZGVub21pbmF0b3IpIH0KCmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKHJhbmdlIEZISVIuUmFuZ2UpOgogICAgaWYgcmFuZ2UgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIEludGVydmFsW1RvUXVhbnRpdHkocmFuZ2UubG93KSwgVG9RdWFudGl0eShyYW5nZS5oaWdoKV0KCmRlZmluZSBmdW5jdGlvbiBUb0NvZGUoY29kaW5nIEZISVIuQ29kaW5nKToKICAgIGlmIGNvZGluZyBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvZGUgewogICAgICAgICAgY29kZTogY29kaW5nLmNvZGUudmFsdWUsCiAgICAgICAgICBzeXN0ZW06IGNvZGluZy5zeXN0ZW0udmFsdWUsCiAgICAgICAgICB2ZXJzaW9uOiBjb2RpbmcudmVyc2lvbi52YWx1ZSwKICAgICAgICAgIGRpc3BsYXk6IGNvZGluZy5kaXNwbGF5LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29uY2VwdChjb25jZXB0IEZISVIuQ29kZWFibGVDb25jZXB0KToKICAgIGlmIGNvbmNlcHQgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5Db25jZXB0IHsKICAgICAgICAgICAgY29kZXM6IGNvbmNlcHQuY29kaW5nIEMgcmV0dXJuIFRvQ29kZShDKSwKICAgICAgICAgICAgZGlzcGxheTogY29uY2VwdC50ZXh0LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZWZlcmVuY2UgU3RyaW5nKToKICAgIGlmIHJlZmVyZW5jZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgUmVmZXJlbmNlIHsgcmVmZXJlbmNlOiBzdHJpbmcgeyB2YWx1ZTogcmVmZXJlbmNlIH0gfQoKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFN0cmluZykgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFJlZmVyZW5jZSkgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZXNvdXJjZSBSZXNvdXJjZSkgcmV0dXJucyBSZWZlcmVuY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24oZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nKSByZXR1cm5zIExpc3Q8RWxlbWVudD46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24ocmVzb3VyY2UgUmVzb3VyY2UsIHVybCBTdHJpbmcpIHJldHVybnMgTGlzdDxFbGVtZW50PjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIGhhc1ZhbHVlKGVsZW1lbnQgRWxlbWVudCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZ2V0VmFsdWUoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEFueTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG9mVHlwZShpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBMaXN0PEFueT46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBpcyhpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gYXMoaWRlbnRpZmllciBTdHJpbmcpIHJldHVybnMgQW55OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZWxlbWVudERlZmluaXRpb24oZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnREZWZpbml0aW9uOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc2xpY2UoZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nLCBuYW1lIFN0cmluZykgcmV0dXJucyBMaXN0PEVsZW1lbnQ+OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMocmVzb3VyY2UgUmVzb3VyY2UpIHJldHVybnMgUmVzb3VyY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhyZXNvdXJjZSBSZXNvdXJjZSwgbW9kaWZpZXIgU3RyaW5nKSByZXR1cm5zIFJlc291cmNlOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnQ6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhlbGVtZW50IEVsZW1lbnQsIG1vZGlmaWVyIFN0cmluZykgcmV0dXJucyBFbGVtZW50OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY29uZm9ybXNUbyhyZXNvdXJjZSBSZXNvdXJjZSwgc3RydWN0dXJlIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29kZSBjb2RlLCB2YWx1ZVNldCBTdHJpbmcpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG1lbWJlck9mKGNvZGluZyBDb2RpbmcsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29uY2VwdCBDb2RlYWJsZUNvbmNlcHQsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZXMoY29kaW5nIENvZGluZywgc3Vic3VtZWRDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lcyhjb25jZXB0IENvZGVhYmxlQ29uY2VwdCwgc3Vic3VtZWRDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZWRCeShjb2RpbmcgQ29kaW5nLCBzdWJzdW1pbmdDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lZEJ5KGNvbmNlcHQgQ29kZWFibGVDb25jZXB0LCBzdWJzdW1pbmdDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gaHRtbENoZWNrcyhlbGVtZW50IEVsZW1lbnQpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY2NvdW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ29uZGl0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Hcm91cGluZ0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25QcmVjaGVja0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVxdWlyZWRCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25TZWxlY3Rpb25CZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpdml0eURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRtaW5pc3RyYXRpdmVHZW5kZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWR2ZXJzZUV2ZW50QWN0dWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFnZ3JlZ2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFwcG9pbnRtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvbkRpcmVjdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uT3BlcmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvblJlc3BvbnNlVHlwZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXVkaXRFdmVudEFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50T3V0Y29tZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW5kaW5nU3RyZW5ndGgpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCdW5kbGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5JbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVRlYW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2xhaW1SZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGluaWNhbEltcHJlc3Npb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVNlYXJjaFN1cHBvcnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUNvbnRlbnRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25Qcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wYXJ0bWVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEVxdWl2YWxlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbERlbGV0ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbFJlYWRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudERhdGFNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRQcm92aXNpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRTdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zdHJhaW50U2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50U3lzdGVtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRhY3RQb2ludFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250cmFjdFB1YmxpY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyaWJ1dG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb3ZlcmFnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDdXJyZW5jeUNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5T2ZXZWVrKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERheXNPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldGVjdGVkSXNzdWVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ29sb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTmFtZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGlhZ25vc3RpY1JlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaXNjcmltaW5hdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudENvbmZpZGVudGlhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5hYmxlV2hlbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlckxvY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmRwb2ludFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXBpc29kZU9mQ2FyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFdmVudENhcGFiaWxpdHlNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50VGltaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2aWRlbmNlVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBvc3VyZVN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4dGVuc2lvbkNvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJBbGxUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGVmaW5lZFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkRldmljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSUmVzb3VyY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJTdWJzdGFuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlZlcnNpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRmFtaWx5SGlzdG9yeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGaWx0ZXJPcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGbGFnU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdvYWxMaWZlY3ljbGVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFJ1bGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cE1lYXN1cmUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JvdXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRhbmNlUmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGVQYWdlR2VuZXJhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhcmFtZXRlckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSFRUUFZlcmIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSWRlbnRpZmllclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGl0eUFzc3VyYW5jZUxldmVsKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltYWdpbmdTdHVkeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSXNzdWVTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVhc3VyZVJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpYVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNaW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYW1lVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFycmF0aXZlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5vdGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlpb25PcmRlckludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOdXRyaXRpb25PcmRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvbkRhdGFUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcGVyYXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvblBhcmFtZXRlclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcmllbnRhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBhcnRpY2lwYW50UmVxdWlyZWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXltZW50Tm90aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9jZWR1cmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvcGVydHlSZXByZXNlbnRhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvdmVuYW5jZUVudGl0eVJvbGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbGl0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbnRpdHlDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VIYW5kbGluZ1BvbGljeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VWZXJzaW9uUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVmZXJyZWREb2N1bWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWxhdGVkQXJ0aWZhY3RUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbWl0dGFuY2VPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcG9zaXRvcnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hFbGVtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaFN0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3ViamVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzb3VyY2VWZXJzaW9uUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3BvbnNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXN0ZnVsQ2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmlza0Fzc2Vzc21lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU1BEWExpY2Vuc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoQ29tcGFyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hFbnRyeU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoTW9kaWZpZXJDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaFBhcmFtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWN0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXF1ZW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbGljaW5nUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2xvdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTb3J0RGlyZWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTcGVjaW1lblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RyYW5kVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcElucHV0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVHJhbnNmb3JtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlEZWxpdmVyeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrSW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRBY3Rpb25SZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UmVzdWx0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRyaWdnZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVEZXJpdmF0aW9uUnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUeXBlUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVESUVudHJ5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVbml0c09mVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvbkJhc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uRXllcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgWFBhdGhVc2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgYmFzZTY0QmluYXJ5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvQm9vbGVhbih2YWx1ZSBib29sZWFuKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZSh2YWx1ZSBkYXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgZGF0ZVRpbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EZWNpbWFsKHZhbHVlIGRlY2ltYWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBpbnN0YW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZWdlcih2YWx1ZSBpbnRlZ2VyKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHN0cmluZyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1RpbWUodmFsdWUgdGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB1cmkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgeGh0bWwpOiB2YWx1ZS52YWx1ZQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iVGhpcyBsaWJyYXJ5IGRlZmluZXMgZnVuY3Rpb25zIHRvIGNvbnZlcnQgYmV0d2VlbiBGSElSJiN4YTsgZGF0YSB0eXBlcyBhbmQgQ1FMIHN5c3RlbS1kZWZpbmVkIHR5cGVzLCBhcyB3ZWxsIGFzIGZ1bmN0aW9ucyB0byBzdXBwb3J0JiN4YTsgRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZTomI3hhOyBodHRwczovL2dpdGh1Yi5jb20vY3FmcmFtZXdvcmsvY2xpbmljYWxfcXVhbGl0eV9sYW5ndWFnZS93aWtpL0ZISVJIZWxwZXJzIi8+CiAgICAgIDxhOnQgbmFtZT0iYWxsb3dGbHVlbnQiIHZhbHVlPSJ0cnVlIi8+CiAgIDwvYW5ub3RhdGlvbj4KICAgPGlkZW50aWZpZXIgaWQ9IkZISVJIZWxwZXJzIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOjEtMTE6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0yMDo2MiIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ6NS0yMDo2MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDo4LTE0OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ6OC0xNDoxMyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTU6OS0xNToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE3OjktMjA6NjIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTc6MTItMTc6MzMiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNzoxMi0xNzoyNSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc6MTItMTc6MTciIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTg6MTMtMTg6NjIiIGxvd0Nsb3NlZD0iZmFsc2UiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTg6MjItMTg6NDEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjIyLTE4OjM1IiBwYXRoPSJzdGFydCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODoyMi0xODoyNyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE4OjQ0LTE4OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODo0NC0xODo1NSIgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjQ0LTE4OjQ5IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjA6MTMtMjA6NjIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIyMDoyMi0yMDo0MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6MjItMjA6MzUiIHBhdGg9InN0YXJ0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjIyLTIwOjI3IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjA6NDQtMjA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjQ0LTIwOjU1IiBwYXRoPSJlbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6NDQtMjA6NDkiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icGVyaW9kIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOjM1LTEzOjQ1IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyOjEtMzM6NyIgbmFtZT0iVG9DYWxlbmRhclVuaXQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOjUtMzM6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y29tcGFyYW5kIGxvY2F0b3I9IjIzOjEwLTIzOjEzIiBuYW1lPSJ1bml0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMjQ6OS0yNDozNiI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjI0OjE0LTI0OjE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibXMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjI0OjI0LTI0OjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibWlsbGlzZWNvbmQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNTo5LTI1OjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjU6MTQtMjU6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJzIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNToyMy0yNTozMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InNlY29uZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI2OjktMjY6MzIiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyNjoxNC0yNjoxOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im1pbiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMjY6MjUtMjY6MzIiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtaW51dGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNzo5LTI3OjI4Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjc6MTQtMjc6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJoIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNzoyMy0yNzoyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImhvdXIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyODo5LTI4OjI3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjg6MTQtMjg6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyODoyMy0yODoyNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImRheSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI5OjktMjk6MjkiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyOToxNC0yOToxNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndrIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyOToyNC0yOToyOSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndlZWsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzMDo5LTMwOjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMzA6MTQtMzA6MTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtbyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMzA6MjQtMzA6MzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtb250aCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjMxOjktMzE6MjgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzMToxNC0zMToxNiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjMxOjIzLTMxOjI4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ieWVhciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMzI6MTQtMzI6MTciIG5hbWU9InVuaXQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjozNy0yMjo0OSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTQ2OjciIG5hbWU9IlRvUXVhbnRpdHkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2OjUtNDY6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMzc6OS0zNzozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM3OjE0LTM3OjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MTQtMzc6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MzYtMzc6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjM4OjktMzg6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzODoxNC0zODozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjE0LTM4OjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODoxNC0zODoyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjQyLTM4OjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzOTo5LTQwOjE4OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM5OjE0LTM5OjQ0IiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzk6MTQtMzk6NDQiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzOToxNC0zOTozMiIgcGF0aD0iY29tcGFyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOToxNC0zOToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTMtNDA6MTg5IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA6MjEtNDA6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDA6MjctNDA6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNDA6MzMtNDA6ODciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkNvbXBhcmF0b3JRdWFudGl0eU5vdFN1cHBvcnRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9IjQwOjkwLTQwOjk2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjQwOjk5LTQwOjE4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNDE6OS00MzoxMzgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI0MToxNC00Mjo5MiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6OTEiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjE0LTQxOjM2IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6MjgiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MToxNC00MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo0MS00MTo5MSIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6NDEtNDE6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQxOjQxLTQxOjU1IiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDE6NDEtNDE6NDgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo2NS00MTo5MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxOC00Mjo5MiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6MTgtNDI6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQyOjE4LTQyOjMyIiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDI6MTgtNDI6MjUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mjo0Mi00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI0MzoxMy00MzoxMzgiIGNsYXNzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6MzgtNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjM4LTQzOjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MzozOC00Mzo0NSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InVuaXQiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6NjYtNDM6MTM2IiBuYW1lPSJUb0NhbGVuZGFyVW5pdCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDM6ODEtNDM6MTM1IiB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mzo5MC00MzoxMDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjkwLTQzOjEwMiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0Mzo5MC00Mzo5NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjExMS00MzoxMjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjExMS00MzoxMjMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDM6MTExLTQzOjExOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjEzMi00MzoxMzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8ZWxzZSBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjIwNSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjIxLTQ1OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjQ1OjI3LTQ1OjMwIiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9IjQ1OjMzLTQ1Ojc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5JbnZhbGlkRkhJUlF1YW50aXR5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8c2V2ZXJpdHkgbG9jYXRvcj0iNDU6NzktNDU6ODUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJFcnJvciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG1lc3NhZ2UgbG9jYXRvcj0iNDU6ODgtNDU6MjA0IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxOTgiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjE3NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTcwIiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxNDYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjEzOSIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxMjEtNDU6MTM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxMjEtNDU6MTMzIiBwYXRoPSJ1bml0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjEyMS00NToxMjgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxNDMtNDU6MTQ2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iICgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE1MC00NToxNzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE1MC00NToxNjQiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxNTAtNDU6MTU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTc0LTQ1OjE3NiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE4MC00NToxOTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE4MC00NToxOTIiIHBhdGg9ImNvZGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDU6MTgwLTQ1OjE4NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjIwMi00NToyMDQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIpIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbWVzc2FnZT4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU6MzctMzU6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTU3OjciIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQ5OjUtNTc6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTA6OS01MDozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUwOjE0LTUwOjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MTQtNTA6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MzYtNTA6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjUxOjktNTE6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MToxNC01MTozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjE0LTUxOjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MToxNC01MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjQyLTUxOjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1Mjo5LTU0OjEzOCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUyOjE0LTUzOjkyIiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01Mjo5MSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTQtNTI6MzYiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01MjoyOCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUyOjE0LTUyOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjQxLTUyOjkxIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Mjo0MS01Mjo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTI6NDEtNTI6NTUiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo0MS01Mjo0OCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjY1LTUyOjkxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjE4LTUzOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxOC01MzozOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6MTgtNTM6MzIiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MzoxOC01MzoyNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjQyLTUzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjU0OjEzLTU0OjEzOCIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDozOC01NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MzgtNTQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjM4LTU0OjQ1IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDo2Ni01NDoxMzYiIG5hbWU9IlRvQ2FsZW5kYXJVbml0IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NDo4MS01NDoxMzUiIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU0OjkwLTU0OjEwOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6OTAtNTQ6MTAyIiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjkwLTU0Ojk3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTExLTU0OjEyOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MTExLTU0OjEyMyIgcGF0aD0idW5pdCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NDoxMTEtNTQ6MTE4IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTMyLTU0OjEzNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTMtNTY6MjA1IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MjEtNTY6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNTY6MjctNTY6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNTY6MzMtNTY6NzYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSI1Njo3OS01Njo4NSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkVycm9yIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8bWVzc2FnZSBsb2NhdG9yPSI1Njo4OC01NjoyMDQiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE5OCIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTc2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxNzAiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE0NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTM5IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxMTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjEyMS01NjoxMzkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjEyMS01NjoxMzMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTIxLTU2OjEyOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjE0My01NjoxNDYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIgKCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTUwLTU2OjE3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTUwLTU2OjE2NCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjE1MC01NjoxNTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NjoxNzQtNTY6MTc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ifCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTgwLTU2OjE5OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTgwLTU2OjE5MiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NjoxODAtNTY6MTg3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MjAyLTU2OjIwNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IikiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9tZXNzYWdlPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0ODo1NS00ODo2NyIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtODQ6MTEiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYwOjUtODQ6MTEiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNjA6OC02MDoyMyIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjYwOjgtNjA6MTUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjA6MzAtNjA6MzMiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI2MTo5LTg0OjExIiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgICAgIDxjb21wYXJhbmQgbG9jYXRvcj0iNjE6MTQtNjE6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjMyIiBwYXRoPSJjb21wYXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvY29tcGFyYW5kPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNjI6MTMtNjY6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI2MjoxOC02MjoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiZsdDsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjYzOjE3LTY2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjQ6MjEtNjQ6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI2NToyMS02NTo1OCIgbmFtZT0iVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjU6NTAtNjU6NTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjY3OjEzLTcxOjE3Ij4KICAgICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iNjc6MTgtNjc6MjEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSImbHQ7PSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iNjg6MTctNzE6MTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgPGxvdyBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY5OjIxLTY5OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iNzA6MjEtNzA6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjcwOjUwLTcwOjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI3MjoxMy03NjoxNyI+CiAgICAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjcyOjE4LTcyOjIxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iPj0iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjczOjE3LTc2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iNzQ6MjEtNzQ6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc0OjUwLTc0OjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzU6MjEtNzU6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNzc6MTMtODE6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI3NzoxOC03NzoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ij4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9Ijc4OjE3LTgxOjE3IiBsb3dDbG9zZWQ9ImZhbHNlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijc5OjIxLTc5OjU4IiBuYW1lPSJUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3OTo1MC03OTo1NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgwOjIxLTgwOjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iODM6MTctODM6NjgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MzoyNi04Mzo0NSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODM6MzctODM6NDQiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODM6NDgtODM6NjciIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjU5LTgzOjY2IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OTozNy01OTo0OSIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg2OjEtOTA6MTA3IiBuYW1lPSJUb1JhdGlvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo1LTkwOjEwNyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSI4Nzo4LTg3OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODc6OC04NzoxMiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjktODg6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTA6OS05MDoxMDciIGNsYXNzVHlwZT0idDpSYXRpbyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ibnVtZXJhdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjM1LTkwOjYxIiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo0Ni05MDo2MCIgcGF0aD0ibnVtZXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjQ2LTkwOjUwIiBuYW1lPSJyYXRpbyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRlbm9taW5hdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjc3LTkwOjEwNSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTA6ODgtOTA6MTA0IiBwYXRoPSJkZW5vbWluYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MDo4OC05MDo5MiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmF0aW8iPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODY6MzEtODY6NDAiIG5hbWU9ImZoaXI6UmF0aW8iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MjoxLTk2OjYzIiBuYW1lPSJUb0ludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mzo1LTk2OjYzIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjkzOjgtOTM6MjAiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mzo4LTkzOjEyIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjktOTQ6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI5Njo5LTk2OjYzIiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTY6MTgtOTY6MzgiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk2OjI5LTk2OjM3IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6MjktOTY6MzMiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Njo0MS05Njo2MiIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTY6NTItOTY6NjEiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6NTItOTY6NTYiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhbmdlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjM0LTkyOjQzIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTg6MS0xMDc6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5OTo1LTEwNzo5IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk5OjgtOTk6MjEiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5OTo4LTk5OjEzIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjktMTAwOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjEwMjo5LTEwNzo5IiBjbGFzc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iY29kZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxMDM6MTctMTAzOjMzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDM6MTctMTAzOjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMzoxNy0xMDM6MjIiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ic3lzdGVtIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDQ6MTktMTA0OjI0IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InZlcnNpb24iPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA1OjIwLTEwNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjIwLTEwNTozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDU6MjAtMTA1OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA2OjIwLTEwNjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA2OjIwLTEwNjozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6MjAtMTA2OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTg6MzEtOTg6NDEiIG5hbWU9ImZoaXI6Q29kaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA5OjEtMTE2OjkiIG5hbWU9IlRvQ29uY2VwdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEwOjUtMTE2OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEwOjgtMTEwOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEwOjgtMTEwOjE0IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExMTo5LTExMToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxMTM6OS0xMTY6OSIgY2xhc3NUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGVzIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjExNDoyMC0xMTQ6NTIiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE0OjIwLTExNDozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTQ6MjAtMTE0OjMzIiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iY29uY2VwdCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxMTQ6MzctMTE0OjUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTE0OjQ0LTExNDo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTQ6NTEiIG5hbWU9IkMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTE1OjIyLTExNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE1OjIyLTExNTozMyIgcGF0aD0idGV4dCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTU6MjItMTE1OjI4IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDk6MzUtMTA5OjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExODoxLTEyMjo2MCIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTk6NS0xMjI6NjAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTE5OjgtMTE5OjI0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE5OjgtMTE5OjE2IiBuYW1lPSJyZWZlcmVuY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMjA6OS0xMjA6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTIyOjktMTIyOjYwIiBjbGFzc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJyZWZlcmVuY2UiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjMyLTEyMjo1OCIgY2xhc3NUeXBlPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjQ4LTEyMjo1NiIgbmFtZT0icmVmZXJlbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzctMTE4OjQyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNDoxLTEyNDo2OCIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzUtMTI0OjQwIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo3MSIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjU6MzUtMTI1OjQzIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNjoxLTEyNjo3MiIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI2OjM2LTEyNjo0MyIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNzoxLTEyNzo4NiIgbmFtZT0iZXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjc6MzUtMTI3OjQxIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjQ4LTEyNzo1MyIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjg6MS0xMjg6ODgiIG5hbWU9ImV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozNi0xMjg6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI4OjUwLTEyODo1NSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjciIG5hbWU9Imhhc1ZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzQtMTI5OjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjMiIG5hbWU9ImdldFZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzQtMTMwOjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzE6MS0xMzE6NjkiIG5hbWU9Im9mVHlwZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkZW50aWZpZXIiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTMxOjM1LTEzMTo0MCIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzI6MS0xMzI6NjMiIG5hbWU9ImlzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWRlbnRpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzI6MzEtMTMyOjM2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMzoxLTEzMzo1OSIgbmFtZT0iYXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZGVudGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMzozMS0xMzM6MzYiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0Ojg2IiBuYW1lPSJlbGVtZW50RGVmaW5pdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjQzLTEzNDo0OSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM1OjEtMTM1Ojk1IiBuYW1lPSJzbGljZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM1OjMxLTEzNTozNyIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0NC0xMzU6NDkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ibmFtZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6NTctMTM1OjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNjoxLTEzNjo3NiIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzY6NDEtMTM2OjQ4IiBuYW1lPSJmaGlyOlJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM3OjEtMTM3OjkzIiBuYW1lPSJjaGVja01vZGlmaWVycyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNzo0MS0xMzc6NDgiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJtb2RpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6NjAtMTM3OjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzODoxLTEzODo3MyIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzODo0MC0xMzg6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo5MCIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo0MC0xMzk6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9Im1vZGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo1OC0xMzk6NjMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQwOjEtMTQwOjg5IiBuYW1lPSJjb25mb3Jtc1RvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQwOjM3LTE0MDo0NCIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN0cnVjdHVyZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6NTctMTQwOjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MToxLTE0MTo3OCIgbmFtZT0ibWVtYmVyT2YiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTozMS0xNDE6MzQiIG5hbWU9ImZoaXI6Y29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTo0Ni0xNDE6NTEiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQyOjEtMTQyOjgyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDI6MzMtMTQyOjM4IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Mjo1MC0xNDI6NTUiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQzOjEtMTQzOjkyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmNlcHQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjM0LTE0Mzo0OCIgbmFtZT0iZmhpcjpDb2RlYWJsZUNvbmNlcHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZVNldCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6NjAtMTQzOjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo4OCIgbmFtZT0ic3Vic3VtZXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDozOCIgbmFtZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJzdWJzdW1lZENvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDQ6NTYtMTQ0OjYxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NToxLTE0NToxMDgiIG5hbWU9InN1YnN1bWVzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzQtMTQ1OjQ4IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWVkQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6NjctMTQ1OjgxIiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NjoxLTE0Njo5MSIgbmFtZT0ic3Vic3VtZWRCeSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6MzUtMTQ2OjQwIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWluZ0NvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6NTktMTQ2OjY0IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NzoxLTE0NzoxMTEiIG5hbWU9InN1YnN1bWVkQnkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NzozNi0xNDc6NTAiIG5hbWU9ImZoaXI6Q29kZWFibGVDb25jZXB0IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ic3Vic3VtaW5nQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6NzAtMTQ3Ojg0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0ODoxLTE0ODo2OSIgbmFtZT0iaHRtbENoZWNrcyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjM2LTE0ODo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjQ4LTE1MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQ4LTE1MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo0NCIgbmFtZT0iZmhpcjpBY2NvdW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUxOjEtMTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUxOjYwLTE1MTo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjYwLTE1MTo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjMyLTE1MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjU0LTE1Mjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjU0LTE1Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1MCIgbmFtZT0iZmhpcjpBY3Rpb25Db25kaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUzOjEtMTUzOjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUzOjU3LTE1Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUzOjU3LTE1Mzo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUzOjMyLTE1Mzo1MyIgbmFtZT0iZmhpcjpBY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU0OjEtMTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU0OjU2LTE1NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU0OjU2LTE1NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU0OjMyLTE1NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NTctMTU1OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NTctMTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjUzIiBuYW1lPSJmaGlyOkFjdGlvblByZWNoZWNrQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTY6MS0xNTY6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTY6NTctMTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTY6NTctMTU2OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTY6MzItMTU2OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTc6MS0xNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTc6NTctMTU3OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTc6NTctMTU3OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTc6MzItMTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTg6MS0xNTg6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTg6NTgtMTU4OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTg6NTgtMTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTg6MzItMTU4OjU0IiBuYW1lPSJmaGlyOkFjdGlvblNlbGVjdGlvbkJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU3LTE1OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU3LTE1OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MyIgbmFtZT0iZmhpcjpBY3Rpdml0eURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjU4LTE2MDo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjU4LTE2MDo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NDIiIG5hbWU9ImZoaXI6QWRkcmVzc1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjI6MS0xNjI6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6NDUtMTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjI6NDUtMTYyOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjI6MzItMTYyOjQxIiBuYW1lPSJmaGlyOkFkZHJlc3NVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NTUtMTYzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NTUtMTYzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY0OjEtMTY0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY0OjU2LTE2NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY0OjU2LTE2NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY0OjMyLTE2NDo1MiIgbmFtZT0iZmhpcjpBZHZlcnNlRXZlbnRBY3R1YWxpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTAtMTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTAtMTY1OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjQ2IiBuYW1lPSJmaGlyOkFnZ3JlZ2F0aW9uTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2NjoxLTE2Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Njo2MS0xNjY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Njo2MS0xNjY6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2NjozMi0xNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjc6MS0xNjc6NzQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjc6NjQtMTY3Ojc0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjc6NjQtMTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjc6MzItMTY3OjYwIiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY4OjEtMTY4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY4OjYxLTE2ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY4OjYxLTE2ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjMyLTE2ODo1NyIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NDgiIG5hbWU9ImZoaXI6QXBwb2ludG1lbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzE6MS0xNzE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzE6NTctMTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzE6NTctMTcxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzE6MzItMTcxOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvbkRpcmVjdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzI6MS0xNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzI6NTYtMTcyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzI6NTYtMTcyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzI6MzItMTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NTUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NDciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo2MS0xNzU6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo2MS0xNzU6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzY6MS0xNzY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzY6NTItMTc2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzY6NTItMTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzY6MzItMTc2OjQ4IiBuYW1lPSJmaGlyOkF1ZGl0RXZlbnRPdXRjb21lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUwLTE3Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUwLTE3Nzo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0NiIgbmFtZT0iZmhpcjpCaW5kaW5nU3RyZW5ndGgiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjktMTc4Ojc5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjktMTc4OjczIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NzciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NjctMTc5Ojc3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NjctMTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjYzIiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTgwOjEtMTgwOjgzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTgwOjczLTE4MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTgwOjczLTE4MDo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTgwOjMyLTE4MDo2OSIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MToxLTE4MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4MTo0NS0xODE6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4MTo0NS0xODE6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MTozMi0xODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6NTQiIG5hbWU9ImZoaXI6Q2FwYWJpbGl0eVN0YXRlbWVudEtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NTUtMTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NTUtMTgzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjUxIiBuYW1lPSJmaGlyOkNhcmVQbGFuQWN0aXZpdHlLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjU3LTE4NDo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjU3LTE4NDo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg1OjEtMTg1OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg1OjQ5LTE4NTo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg1OjQ5LTE4NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg1OjMyLTE4NTo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhbkludGVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0OS0xODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0OS0xODY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODc6MS0xODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODc6NDktMTg3OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODc6NDktMTg3OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODc6MzItMTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg4OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg4OjU5LTE4ODo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg4OjU5LTE4ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjMyLTE4ODo1NSIgbmFtZT0iZmhpcjpDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODk6MS0xODk6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODk6NzMtMTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODk6NzMtMTg5Ojc3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODk6MzItMTg5OjY5IiBuYW1lPSJmaGlyOkNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkwOjEtMTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkwOjUxLTE5MDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkwOjUxLTE5MDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkwOjMyLTE5MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjU0LTE5MTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjU0LTE5MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo1MCIgbmFtZT0iZmhpcjpDbGFpbVJlc3BvbnNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkyOjEtMTkyOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkyOjMyLTE5Mjo0MiIgbmFtZT0iZmhpcjpDbGFpbVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk0OjEtMTk0OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk0OjUyLTE5NDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk0OjUyLTE5NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk0OjMyLTE5NDo0OCIgbmFtZT0iZmhpcjpDb2RlU2VhcmNoU3VwcG9ydCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NToxLTE5NTo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NTozMi0xOTU6NTIiIG5hbWU9ImZoaXI6Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk2OjEtMTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk2OjYxLTE5Njo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk2OjYxLTE5Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjMyLTE5Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NzoxLTE5Nzo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NzozMi0xOTc6NTIiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjYxLTE5ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjYxLTE5ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo1NyIgbmFtZT0iZmhpcjpDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5OToxLTE5OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OTo1NC0xOTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5OTo1NC0xOTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5OTozMi0xOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMDoxLTIwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMDo1MC0yMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMDo1MC0yMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMDozMi0yMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAxOjEtMjAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAxOjUwLTIwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAxOjUwLTIwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAxOjMyLTIwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NjEtMjAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NjEtMjAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAzOjEtMjAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAzOjUyLTIwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAzOjUyLTIwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAzOjMyLTIwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwNDoxLTIwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNDozMi0yMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjYyLTIwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjYyLTIwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NTgtMjA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NTgtMjA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTMtMjA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTMtMjA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOToxLTIwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwOTo1NS0yMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOTo1NS0yMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwOTozMi0yMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTA6MS0yMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTA6NDctMjEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTA6NDctMjEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTA6MzItMjEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxMToxLTIxMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxMTo1My0yMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxMTo1My0yMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxMTozMi0yMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjUzLTIxMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjUzLTIxMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTM6MS0yMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTM6NTAtMjEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTM6NTAtMjEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTM6MzItMjEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNDoxLTIxNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNDozMi0yMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTY6MS0yMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTY6NTAtMjE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6NTAtMjE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTY6MzItMjE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNzoxLTIxNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNzozMi0yMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTg6MS0yMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTg6NDctMjE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTg6NDctMjE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTg6MzItMjE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo0NC0yMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo0NC0yMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIwOjEtMjIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIwOjQ1LTIyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIwOjQ1LTIyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIwOjMyLTIyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjU2LTIyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjU2LTIyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTQtMjIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTQtMjIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NjMtMjIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NjMtMjIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjQ6MS0yMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjQ6NjItMjI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjQ6NjItMjI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjQ6MzItMjI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNToxLTIyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNTo1NS0yMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNTo1NS0yMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNTozMi0yMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjY6MS0yMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6NTItMjI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjY6NTItMjI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjY6MzItMjI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI3OjEtMjI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI3OjY0LTIyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI3OjY0LTIyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI3OjMyLTIyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyODoxLTIyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyODo0OS0yMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyODo0OS0yMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyODozMi0yMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjk6MS0yMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjk6NTQtMjI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjk6NTQtMjI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjk6MzItMjI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NTktMjMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NTktMjMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMjoxLTIzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMjozMi0yMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzM6MS0yMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzM6NTgtMjMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzM6NTgtMjMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzM6MzItMjMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM0OjEtMjM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjQ3LTIzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM0OjQ3LTIzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM0OjMyLTIzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzU6MS0yMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzU6NTgtMjM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzU6NTgtMjM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzU6MzItMjM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM2OjEtMjM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM2OjU5LTIzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM2OjU5LTIzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM2OjMyLTIzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzc6MS0yMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzc6NjAtMjM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzc6NjAtMjM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzc6MzItMjM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzg6MS0yMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzg6NTktMjM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzg6NTktMjM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzg6MzItMjM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOToxLTIzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOTo2MS0yMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzOTo2MS0yMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzOTozMi0yMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NjAtMjQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NjAtMjQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTMtMjQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTMtMjQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MjoxLTI0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MjozMi0yNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDM6MS0yNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDM6NTAtMjQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDM6NTAtMjQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDM6MzItMjQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NDoxLTI0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NDozMi0yNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NTgtMjQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NTgtMjQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ2OjEtMjQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ2OjU5LTI0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ2OjU5LTI0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ2OjMyLTI0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDc6MS0yNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDc6NTQtMjQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDc6NTQtMjQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDc6MzItMjQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDg6MS0yNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDg6NTQtMjQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDg6NTQtMjQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDg6MzItMjQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NDYtMjQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NDYtMjQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUwOjEtMjUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUwOjU1LTI1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUwOjU1LTI1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUwOjMyLTI1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjYxLTI1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjYxLTI1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MzoxLTI1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MzozMi0yNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NDoxLTI1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NDozMi0yNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NDctMjU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NDctMjU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NjoxLTI1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Njo1MC0yNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Njo1MC0yNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NjozMi0yNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjUxLTI1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjUxLTI1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU4OjEtMjU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU4OjUxLTI1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU4OjUxLTI1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU4OjMyLTI1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjU0LTI1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjU0LTI1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ2LTI2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ2LTI2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MToxLTI2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MTo1NC0yNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2MTo1NC0yNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTozMi0yNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MjoxLTI2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MjozMi0yNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjM6MS0yNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjM6NDUtMjYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjM6NDUtMjYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjM6MzItMjYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NTQtMjY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NTQtMjY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTUtMjY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTUtMjY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU0LTI2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU0LTI2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY3OjEtMjY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY3OjMyLTI2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjg6MS0yNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjg6NDQtMjY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjg6NDQtMjY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjg6MzItMjY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2OToxLTI2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2OTozMi0yNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MDoxLTI3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDo1NC0yNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MDo1NC0yNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MDozMi0yNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MToxLTI3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MTo1My0yNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MTo1My0yNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MTozMi0yNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcyOjEtMjcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjQzLTI3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcyOjQzLTI3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcyOjMyLTI3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MzoxLTI3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MzozMi0yNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NToxLTI3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NTo1My0yNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NTo1My0yNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NTozMi0yNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc2OjEtMjc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc2OjYzLTI3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc2OjYzLTI3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc2OjMyLTI3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc3OjEtMjc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc3OjUzLTI3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc3OjUzLTI3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc3OjMyLTI3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzg6MS0yNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzg6NjAtMjc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzg6NjAtMjc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzg6MzItMjc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzk6MS0yNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzk6NDgtMjc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzk6NDgtMjc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzItMjc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDgtMjgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDgtMjgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NDQtMjgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NDQtMjgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4MjoxLTI4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Mjo0My0yODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Mjo0My0yODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4MjozMi0yODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NDYtMjgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NDYtMjgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg0OjEtMjg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg0OjQzLTI4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg0OjQzLTI4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg0OjMyLTI4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NToxLTI4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4NTo0NS0yODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4NTo0NS0yODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NTozMi0yODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg3OjEtMjg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg3OjMyLTI4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1NC0yODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1NC0yODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo1Mi0yODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo1Mi0yODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTA6MS0yOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTA6NDYtMjkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTA6NDYtMjkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTA6MzItMjkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkxOjEtMjkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkxOjY1LTI5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkxOjY1LTI5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkxOjMyLTI5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTI6MS0yOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTI6NTktMjkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTI6NTktMjkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTI6MzItMjkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MzoxLTI5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MzozMi0yOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NDoxLTI5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NDozMi0yOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NjAtMjk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NjAtMjk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTY6MS0yOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTY6NTgtMjk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTY6NTgtMjk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTY6MzItMjk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk3OjEtMjk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk3OjYwLTI5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk3OjYwLTI5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjMyLTI5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk4OjEtMjk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk4OjUxLTI5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk4OjUxLTI5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk4OjMyLTI5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk5OjEtMjk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjYyLTI5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjYyLTI5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMyLTI5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDA6MS0zMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDA6NjUtMzAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDA6NjUtMzAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDA6MzItMzAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMTo0My0zMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMTo0My0zMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMTozMi0zMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDI6MS0zMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDItMzAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDItMzAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzItMzAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDM6NjEtMzAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDM6NjEtMzAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDM6MzItMzAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA0OjEtMzA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA0OjUxLTMwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA0OjUxLTMwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA0OjMyLTMwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA1OjUwLTMwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA1OjUwLTMwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjMyLTMwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDY6MS0zMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6NDMtMzA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6NDMtMzA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzItMzA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA3OjEtMzA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA3OjU2LTMwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA3OjU2LTMwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA3OjMyLTMwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDg6MS0zMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDg6NTUtMzA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDg6NTUtMzA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDg6MzItMzA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA5OjEtMzA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA5OjU0LTMwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA5OjU0LTMwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA5OjMyLTMwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEwOjEtMzEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEwOjU5LTMxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEwOjU5LTMxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEwOjMyLTMxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTE6MS0zMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTE6NTItMzExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTE6NTItMzExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTE6MzItMzExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEyOjEtMzEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEyOjQ4LTMxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEyOjQ4LTMxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEyOjMyLTMxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEzOjEtMzEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEzOjU2LTMxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEzOjU2LTMxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEzOjMyLTMxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTQ6MS0zMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTQ6NTAtMzE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTQ6NTAtMzE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTQ6MzItMzE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxNTozMi0zMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE2OjEtMzE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE2OjU0LTMxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE2OjU0LTMxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE2OjMyLTMxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE3OjEtMzE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE3OjUyLTMxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE3OjUyLTMxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE3OjMyLTMxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxODoxLTMxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxODo1NC0zMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxODo1NC0zMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxODozMi0zMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxOToxLTMxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxOTo1NC0zMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxOTo1NC0zMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxOTozMi0zMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyMDoxLTMyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyMDozMi0zMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzIxOjEtMzIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzIxOjUwLTMyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzIxOjUwLTMyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzIxOjMyLTMyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjI6MS0zMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjI6NTctMzIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjI6NTctMzIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjI6MzItMzIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjM6MS0zMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjM6NDctMzIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjM6NDctMzIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjM6MzItMzIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNDoxLTMyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNDozMi0zMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjU6MS0zMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjU6NTItMzI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjU6NTItMzI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjU6MzItMzI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI2OjEtMzI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI2OjQ2LTMyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI2OjQ2LTMyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI2OjMyLTMyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNzoxLTMyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNzo1My0zMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNzo1My0zMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNzozMi0zMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI4OjEtMzI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI4OjYwLTMyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI4OjYwLTMyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI4OjMyLTMyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI5OjEtMzI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI5OjU2LTMyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI5OjU2LTMyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI5OjMyLTMyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzA6MS0zMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzA6NjItMzMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzA6NjItMzMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzA6MzItMzMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMToxLTMzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMTozMi0zMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzI6MS0zMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzI6NTYtMzMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzI6NTYtMzMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzI6MzItMzMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMzoxLTMzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMzozMi0zMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNDoxLTMzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNDozMi0zMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNToxLTMzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNTozMi0zMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzY6MS0zMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzY6NDktMzM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzY6NDktMzM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzY6MzItMzM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM3OjEtMzM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM3OjQ4LTMzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM3OjQ4LTMzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM3OjMyLTMzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM4OjEtMzM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM4OjUwLTMzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM4OjUwLTMzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM4OjMyLTMzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzk6MS0zMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzk6NDgtMzM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzk6NDgtMzM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzk6MzItMzM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDA6MS0zNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDA6NTQtMzQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDA6NTQtMzQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDA6MzItMzQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDE6MS0zNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDE6NTQtMzQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDE6NTQtMzQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDE6MzItMzQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDI6MS0zNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDI6NTYtMzQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDI6NTYtMzQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDI6MzItMzQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0MzoxLTM0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0MzozMi0zNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ0OjEtMzQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ0OjU2LTM0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ0OjU2LTM0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ0OjMyLTM0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDU6MS0zNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDU6NDctMzQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDU6NDctMzQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDU6MzItMzQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0NjoxLTM0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0NjozMi0zNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ3OjEtMzQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ3OjU1LTM0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ3OjU1LTM0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ3OjMyLTM0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0ODoxLTM0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0ODozMi0zNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDk6MS0zNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDk6NTEtMzQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDk6NTEtMzQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDk6MzItMzQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTA6MS0zNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTA6NTAtMzUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTA6NTAtMzUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTA6MzItMzUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1MToxLTM1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1MTo1My0zNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1MTo1My0zNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1MTozMi0zNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzUyOjEtMzUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzUyOjUwLTM1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzUyOjUwLTM1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzUyOjMyLTM1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTM6MS0zNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTM6NDYtMzUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTM6NDYtMzUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTM6MzItMzUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU0OjEtMzU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU0OjQ3LTM1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU0OjQ3LTM1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU0OjMyLTM1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTU6MS0zNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTU6NTUtMzU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTU6NTUtMzU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTU6MzItMzU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU2OjEtMzU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU2OjU3LTM1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU2OjU3LTM1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU2OjMyLTM1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU3OjEtMzU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU3OjU1LTM1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU3OjU1LTM1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU3OjMyLTM1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1ODoxLTM1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1ODozMi0zNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU5OjEtMzU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU5OjQ1LTM1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU5OjQ1LTM1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU5OjMyLTM1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYwOjEtMzYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYwOjQ4LTM2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYwOjQ4LTM2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYwOjMyLTM2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYxOjEtMzYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYxOjYyLTM2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYxOjYyLTM2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYxOjMyLTM2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjI6MS0zNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjI6NDktMzYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjI6NDktMzYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjI6MzItMzYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYzOjEtMzYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYzOjQxLTM2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYzOjQxLTM2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYzOjMyLTM2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjQ6MS0zNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjQ6NDUtMzY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjQ6NDUtMzY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjQ6MzItMzY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjU6MS0zNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjU6NTgtMzY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjU6NTgtMzY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjU6MzItMzY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY2OjEtMzY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY2OjU4LTM2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY2OjU4LTM2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY2OjMyLTM2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2NzoxLTM2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2NzozMi0zNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2ODoxLTM2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2ODozMi0zNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY5OjEtMzY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY5OjU2LTM2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY5OjU2LTM2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY5OjMyLTM2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNzA6MS0zNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNzA6NjEtMzcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzA6NjEtMzcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNzA6MzItMzcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzcxOjEtMzcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzcxOjYxLTM3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzcxOjYxLTM3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzcxOjMyLTM3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3MjoxLTM3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3MjozMi0zNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzczOjEtMzczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzczOjU4LTM3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzczOjU4LTM3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzczOjMyLTM3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NDoxLTM3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3NDo1My0zNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3NDo1My0zNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NDozMi0zNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc1OjEtMzc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc1OjU1LTM3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc1OjU1LTM3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc1OjMyLTM3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NjoxLTM3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Njo1NC0zNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Njo1NC0zNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NjozMi0zNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NzoxLTM3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NzozMi0zNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc4OjEtMzc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc4OjQ1LTM3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc4OjQ1LTM3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc4OjMyLTM3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc5OjEtMzc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc5OjQ3LTM3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc5OjQ3LTM3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc5OjMyLTM3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODA6MS0zODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODA6NDUtMzgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODA6NDUtMzgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODA6MzItMzgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODE6MS0zODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODE6NTctMzgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODE6NTctMzgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODE6MzItMzgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODI6MS0zODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODI6NjAtMzgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODI6NjAtMzgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODI6MzItMzgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODM6MS0zODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODM6NTEtMzgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODM6NTEtMzgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODM6MzItMzgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODQ6MS0zODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODQ6NTEtMzg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODQ6NTEtMzg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODQ6MzItMzg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODU6MS0zODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODU6NjItMzg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODU6NjItMzg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODU6MzItMzg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4NjoxLTM4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4NjozMi0zODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODc6MS0zODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODc6NTMtMzg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODc6NTMtMzg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODc6MzItMzg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4ODoxLTM4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4ODozMi0zODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4OToxLTM4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4OTozMi0zODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkwOjEtMzkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkwOjQ2LTM5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkwOjQ2LTM5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkwOjMyLTM5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5MToxLTM5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5MTozOC0zOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5MTozOC0zOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5MTozMi0zOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkyOjEtMzkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkyOjMyLTM5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTM6MS0zOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTM6NDUtMzkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTM6NDUtMzkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTM6MzItMzkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTQ6MS0zOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTQ6NDUtMzk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTQ6NDUtMzk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTQ6MzItMzk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTU6MS0zOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTU6NDctMzk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTU6NDctMzk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTU6MzItMzk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5NjoxLTM5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5NjozMi0zOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTc6MS0zOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTc6NDctMzk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTc6NDctMzk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTc6MzItMzk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5ODoxLTM5ODo1MyIgbmFtZT0iVG9Cb29sZWFuIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTg6NDMtMzk4OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTg6NDMtMzk4OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTg6MzMtMzk4OjM5IiBuYW1lPSJmaGlyOmJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTk6MS0zOTk6NDciIG5hbWU9IlRvRGF0ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzk5OjM3LTM5OTo0NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzk5OjM3LTM5OTo0MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzk5OjMwLTM5OTozMyIgbmFtZT0iZmhpcjpkYXRlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAwOjEtNDAwOjU1IiBuYW1lPSJUb0RhdGVUaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDA6NDUtNDAwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MDA6NDUtNDAwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0MDA6MzQtNDAwOjQxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAxOjEtNDAxOjUzIiBuYW1lPSJUb0RlY2ltYWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMTo0My00MDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMTo0My00MDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMTozMy00MDE6MzkiIG5hbWU9ImZoaXI6ZGVjaW1hbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwMjoxLTQwMjo1NCIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDAyOjQ0LTQwMjo1NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDAyOjQ0LTQwMjo0OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDAyOjM0LTQwMjo0MCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAzOjEtNDAzOjUzIiBuYW1lPSJUb0ludGVnZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMzo0My00MDM6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMzo0My00MDM6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMzozMy00MDM6MzkiIG5hbWU9ImZoaXI6aW50ZWdlciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNDoxLTQwNDo1MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNDo0MS00MDQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNDo0MS00MDQ6NDUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNDozMi00MDQ6MzciIG5hbWU9ImZoaXI6c3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA1OjEtNDA1OjQ3IiBuYW1lPSJUb1RpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNTozNy00MDU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNTozNy00MDU6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNTozMC00MDU6MzMiIG5hbWU9ImZoaXI6dGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNjoxLTQwNjo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNjozOC00MDY6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNjozOC00MDY6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNjozMi00MDY6MzQiIG5hbWU9ImZoaXI6dXJpIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA3OjEtNDA3OjUwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDA3OjQwLTQwNzo1MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA3OjQwLTQwNzo0NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDA3OjMyLTQwNzozNiIgbmFtZT0iZmhpcjp4aHRtbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" + }, + { + "contentType": "application/elm+json", + "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgbGlicmFyeSBkZWZpbmVzIGZ1bmN0aW9ucyB0byBjb252ZXJ0IGJldHdlZW4gRkhJUlxuIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydFxuIEZISVJQYXRoIGltcGxlbWVudGF0aW9uLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIHRoZSBGSElSSGVscGVycyB3aWtpIHBhZ2U6XG4gaHR0cHM6Ly9naXRodWIuY29tL2NxZnJhbWV3b3JrL2NsaW5pY2FsX3F1YWxpdHlfbGFuZ3VhZ2Uvd2lraS9GSElSSGVscGVycyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiYWxsb3dGbHVlbnQiLAogICAgICAgICAgICAidmFsdWUiIDogInRydWUiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiwKICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTExOjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAic3RhdGVtZW50cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzoxLTIwOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6NS0yMDo2MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6OC0xNDoyMSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDo4LTE0OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6OS0xNToxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo5LTIwOjYyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6MTItMTc6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxMy0xODo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MjItMTg6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjIyLTE4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoyMi0xODoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo0NC0xODo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJlbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxMy0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoyMi0yMDo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MjItMjA6MzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjIyLTIwOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjQ0LTIwOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzozNS0xMzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTMzOjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NS0zMzo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY29tcGFyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEwLTIzOjEzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6OS0yNDozNiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjE0LTI0OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjI0LTI0OjM2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbGxpc2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6OS0yNTozMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjE0LTI1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6MjMtMjU6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAic2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6OS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjE0LTI2OjE4IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyNS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtaW51dGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzo5LTI3OjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6MTQtMjc6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyMy0yNzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJob3VyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6OS0yODoyNyIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OjE0LTI4OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MjMtMjg6MjciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZGF5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6OS0yOToyOSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjE0LTI5OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndrIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjI0LTI5OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndlZWsiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo5LTMwOjMwIiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTQtMzA6MTciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MjQtMzA6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW9udGgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTo5LTMxOjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTQtMzE6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiYSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToyMy0zMToyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxNC0zMjoxNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjM3LTIyOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS00Njo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6NS00Njo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzo5LTM3OjM5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6OS0zODo0NSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxNC0zODoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDItMzg6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjktNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTMtNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjIxLTQwOjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MjctNDA6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MzMtNDA6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5Db21wYXJhdG9yUXVhbnRpdHlOb3RTdXBwb3J0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjkwLTQwOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjk5LTQwOjE4OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSIFF1YW50aXR5IHZhbHVlIGhhcyBhIGNvbXBhcmF0b3IgYW5kIGNhbm5vdCBiZSBjb252ZXJ0ZWQgdG8gYSBTeXN0ZW0uUXVhbnRpdHkgdmFsdWUuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjktNDM6MTM4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MTQtNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MTozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo0MS00MTo5MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjQxLTQxOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjY1LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxOC00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjE4LTQyOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjQyLTQyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMy00MzoxMzgiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzozOC00Mzo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MzgtNDM6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjM4LTQzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6NjYtNDM6MTM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ2FsZW5kYXJVbml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo4MS00MzoxMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6OTAtNDM6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjExMS00MzoxMTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjEzMi00MzoxMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxMy00NToyMDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyNy00NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Qm9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAidHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29kZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozMy00NTo3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojc5LTQ1Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTIxLTQ1OjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTQzLTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTUwLTQ1OjE1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTc0LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ8IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTgwLTQ1OjE4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjAyLTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzctMzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTU3OjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0OTo1LTU3OjciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICJjYXNlSXRlbSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjktNTA6MzkiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyOSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjM2LTUwOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo5LTUxOjQ1IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MzUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjE0LTUxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0Mi01MTo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6OS01NDoxMzgiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxNC01Mzo5MiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjQxLTUyOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDEtNTI6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NjUtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjE4LTUzOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTgtNTM6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NDItNTM6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEzLTU0OjEzOCIsCiAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjM4LTU0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDozOC01NDo1MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MzgtNTQ6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo2Ni01NDoxMzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjgxLTU0OjEzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo5MC01NDoxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTExLTU0OjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTMyLTU0OjEzNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEzLTU2OjIwNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMS01NjoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjI3LTU2OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMzLTU2Ojc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NzktNTY6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkludmFsaWQgRkhJUiBRdWFudGl0eSBjb2RlOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxMjEtNTY6MTI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNDMtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiAoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNTAtNTY6MTU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNzQtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxODAtNTY6MTg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMDItNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo1NS00ODo2NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtODQ6MTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1LTg0OjExIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo4LTYwOjIzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjgtNjA6MTUiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMwLTYwOjMzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjktODQ6MTEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICAgICJjb21wYXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MTQtNjE6MzgiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MjoxMy02NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjE4LTYyOjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjwiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MTctNjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjIxLTY0OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MjEtNjU6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzoxMy03MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjE4LTY3OjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjw9IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjE3LTcxOjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjIxLTY5OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MjEtNzA6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTAtNzA6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxMy03NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjE4LTcyOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj49IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjE3LTc2OjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjIxLTc0OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUwLTc0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToyMS03NToyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzoxMy04MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjE4LTc3OjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj4iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTctODE6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjIxLTc5OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjUwLTc5OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDoyMS04MDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjE3LTgzOjY4IiwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI2LTgzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjM3LTgzOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo0OC04Mzo2NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1OS04Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozNy01OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjEtOTA6MTA3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUmF0aW8iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NS05MDoxMDciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjktODg6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo5LTkwOjEwNyIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIm51bWVyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDozNS05MDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo0Ni05MDo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjQ2LTkwOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NzctOTA6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjg4LTkwOjEwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkZW5vbWluYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6ODgtOTA6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMxLTg2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MS05Njo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjUtOTY6NjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjgtOTM6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6OC05MzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6OS05NDoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo5LTk2OjYzIiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjE4LTk2OjM4IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjI5LTk2OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MjktOTY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NDEtOTY6NjIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MzQtOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoxLTEwNzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1LTEwNzo5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo4LTk5OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjgtOTk6MTMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6OS0xMDA6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6OS0xMDc6OSIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzoxNy0xMDM6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjE5LTEwNDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToyMC0xMDU6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MjAtMTA2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMS05ODo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MS0xMTY6OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjUtMTE2OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDo4LTExMDoyMiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6OC0xMTA6MTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjktMTExOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjktMTE2OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGVzIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjIwLTExNDozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozNy0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6NDQtMTE0OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidGV4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjIyLTExNToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MzUtMTA5OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTEyMjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjUtMTIyOjYwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6OC0xMTk6MjQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjgtMTE5OjE2IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIwOjktMTIwOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjktMTIyOjYwIiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjQ4LTEyMjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzctMTE4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI0OjEtMTI0OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDozNS0xMjQ6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MS0xMjU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAicmVzb2x2ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI1OjM1LTEyNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjY6MS0xMjY6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozNi0xMjY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6ODYiLAogICAgICAgICAgICAibmFtZSIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjM1LTEyNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjQ4LTEyNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo4OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI4OjM2LTEyODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODo1MC0xMjg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MS0xMjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiaGFzVmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MzQtMTI5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MS0xMzA6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiZ2V0VmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzQtMTMwOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MS0xMzE6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAib2ZUeXBlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkZW50aWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMxOjM1LTEzMTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjoxLTEzMjo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjozMS0xMzI6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MS0xMzM6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiYXMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzEtMTMzOjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0Ojg2IiwKICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnREZWZpbml0aW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjQzLTEzNDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1Ojk1IiwKICAgICAgICAgICAgIm5hbWUiIDogInNsaWNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjMxLTEzNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ0LTEzNTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibmFtZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzU6NTctMTM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjEtMTM2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImNoZWNrTW9kaWZpZXJzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo0MS0xMzY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6OTMiLAogICAgICAgICAgICAibmFtZSIgOiAiY2hlY2tNb2RpZmllcnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjQxLTEzNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODoxLTEzODo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo0MC0xMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxLTEzOTo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo0MC0xMzk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjU4LTEzOTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxLTE0MDo4OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjb25mb3Jtc1RvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNy0xNDA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN0cnVjdHVyZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6NTctMTQwOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjc4IiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjMxLTE0MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1jb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6NDYtMTQxOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MzMtMTQyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWVTZXQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjUwLTE0Mjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoxLTE0Mzo5MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJtZW1iZXJPZiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzozNC0xNDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZWFibGVDb25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6NjAtMTQzOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEtMTQ0Ojg4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MzMtMTQ0OjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtZWRDb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjU2LTE0NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MS0xNDU6MTA4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjM0LTE0NTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVkQ29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NjctMTQ1OjgxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxLTE0Njo5MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MzUtMTQ2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtaW5nQ29kaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjExMSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjM2LTE0Nzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWluZ0NvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjcwLTE0Nzo4NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MS0xNDg6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiaHRtbENoZWNrcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODozNi0xNDg6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjY291bnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MS0xNTE6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjMyLTE1MTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzoxLTE1Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MzItMTUzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTQ6MS0xNTQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjMyLTE1NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjEtMTU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NjozMi0xNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NzoxLTE1Nzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MzItMTU3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlcXVpcmVkQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTg6MS0xNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjMyLTE1ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5RGVmaW5pdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjEtMTYyOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MjozMi0xNjI6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRkcmVzc1VzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkbWluaXN0cmF0aXZlR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjEtMTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDozMi0xNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdncmVnYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjEtMTY2OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NjozMi0xNjY6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjc6MS0xNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjMyLTE2Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODoxLTE2ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6MzItMTY4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjEtMTcxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTozMi0xNzE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MjoxLTE3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MzItMTcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbk9wZXJhdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzY6MS0xNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjMyLTE3Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpbmRpbmdTdHJlbmd0aCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4OjczIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDoxLTE4MDo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjgzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MzItMTgwOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgxOjEtMTgxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTozMi0xODE6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QnVuZGxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5BY3Rpdml0eUtpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg1OjEtMTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTozMi0xODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NzoxLTE4Nzo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MzItMTg3OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVUZWFtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg4OjEtMTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODozMi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjEtMTg5OjgzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTozMi0xODk6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MS0xOTA6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjMyLTE5MDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DaGFyZ2VJdGVtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjoxLTE5Mjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6MzItMTkyOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNsYWltU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk0OjEtMTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDozMi0xOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTU6MS0xOTU6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjMyLTE5NTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtQ29udGVudE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MS0xOTY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjMyLTE5Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzoxLTE5Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6MzItMTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjEtMTk5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozMi0xOTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tbXVuaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMDoxLTIwMDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MzItMjAwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMToxLTIwMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MzItMjAxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjEtMjAzOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzozMi0yMDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MS0yMDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjMyLTIwNDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwRXF1aXZhbGVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb25hbERlbGV0ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsUmVhZFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnREYXRhTWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOToxLTIwOTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6MzItMjA5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnRQcm92aXNpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEwOjEtMjEwOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDozMi0yMTA6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjExOjEtMjExOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTozMi0yMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc3RyYWludFNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50U3lzdGVtIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEzOjEtMjEzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzozMi0yMTM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE0OjEtMjE0OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDozMi0yMTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnRyYWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjEtMjE2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjozMi0yMTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJpYnV0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjEtMjE3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzozMi0yMTc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q292ZXJhZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MS0yMTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjMyLTIxODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DdXJyZW5jeUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjA6MS0yMjA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjMyLTIyMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlzT2ZXZWVrIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MS0yMjQ6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjMyLTIyNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6MS0yMjU6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjMyLTIyNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNjoxLTIyNjo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MzItMjI2OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NvbG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI3OjEtMjI3Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzozMi0yMjc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MS0yMjg6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjMyLTIyODo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VOYW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOToxLTIyOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MzItMjI5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaWFnbm9zdGljUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMyOjEtMjMyOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjozMi0yMzI6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGlzY3JpbWluYXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MS0yMzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjMyLTIzMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudENvbmZpZGVudGlhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDoxLTIzNDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MzItMjM0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNToxLTIzNTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MzItMjM1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjEtMjM2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjozMi0yMzY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM3OjEtMjM3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzozMi0yMzc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzODoxLTIzODo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MzItMjM4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOToxLTIzOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MzItMjM5OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuYWJsZVdoZW5CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MjoxLTI0Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MzItMjQyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuY291bnRlckxvY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjEtMjQzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MzozMi0yNDM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjEtMjQ0OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDozMi0yNDQ6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5kcG9pbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbnJvbGxtZW50UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NjoxLTI0Njo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MzItMjQ2OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NzoxLTI0Nzo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MzItMjQ3OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVwaXNvZGVPZkNhcmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MS0yNDg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjMyLTI0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXZlbnRUaW1pbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MS0yNTA6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjMyLTI1MDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmlkZW5jZVZhcmlhYmxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjEtMjUzOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzozMi0yNTM6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwb3N1cmVTdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxLTI1NDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6MzItMjU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4dGVuc2lvbkNvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkFsbFR5cGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjEtMjU2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NjozMi0yNTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRlZmluZWRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRldmljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1ODoxLTI1ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MzItMjU4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJSZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSU3Vic3RhbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlZlcnNpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MS0yNjE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjMyLTI2MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GYW1pbHlIaXN0b3J5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjEtMjYyOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozMi0yNjI6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RmlsdGVyT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjM6MS0yNjM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjMyLTI2Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GbGFnU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R29hbExpZmVjeWNsZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyYXBoQ29tcGFydG1lbnRSdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NzoxLTI2Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MzItMjY3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwTWVhc3VyZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODoxLTI2ODo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6MzItMjY4OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI2OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6MzItMjY5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRhbmNlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6MS0yNzA6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjMyLTI3MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhZ2VHZW5lcmF0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjEtMjcxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTozMi0yNzE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGVQYXJhbWV0ZXJDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcyOjEtMjcyOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozMi0yNzI6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SFRUUFZlcmIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MS0yNzM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjMyLTI3Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGlmaWVyVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NToxLTI3NTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6MzItMjc1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltYWdpbmdTdHVkeVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NjoxLTI3Njo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MzItMjc2OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzc6MS0yNzc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjMyLTI3Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbW11bml6YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzg6MS0yNzg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjMyLTI3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjEtMjc5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozMi0yNzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW52b2ljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Jc3N1ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODI6MS0yODI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjMyLTI4MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaW5rVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmthZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjEtMjg0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDozMi0yODQ6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODU6MS0yODU6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjMyLTI4NTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjEtMjg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzozMi0yODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVhc3VyZVJlcG9ydFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTA6MS0yOTA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjMyLTI5MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpYVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MToxLTI5MTo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjc1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MzItMjkxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MjoxLTI5Mjo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MzItMjkyOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MzoxLTI5Mzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MzItMjkzOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTQ6MS0yOTQ6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjMyLTI5NDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdEludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MS0yOTY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjMyLTI5Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzoxLTI5Nzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6MzItMjk3OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MS0yOTg6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMyLTI5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk5OjEtMjk5OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMi0yOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjEtMzAwOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDozMi0zMDA6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTozMi0zMDE6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWltZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDI6MS0zMDI6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo1MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMyLTMwMjozOCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1lVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzozMi0zMDM6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MS0zMDQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjMyLTMwNDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjEtMzA1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTozMi0zMDU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFycmF0aXZlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA2OjEtMzA2OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMi0zMDY6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Tm90ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MS0zMDc6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjMyLTMwNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpaW9uT3JkZXJJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MS0zMDg6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMyLTMwODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpb25PcmRlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOToxLTMwOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6MzItMzA5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uRGF0YVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTA6MS0zMTA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjMyLTMxMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTE6MS0zMTE6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjMyLTMxMTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxMjoxLTMxMjo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6MzItMzEyOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTM6MS0zMTM6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjMyLTMxMzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcGVyYXRpb25QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTQ6MS0zMTQ6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjMyLTMxNDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcmllbnRhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjMyLTMxNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6MS0zMTY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjMyLTMxNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFJlcXVpcmVkIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE3OjEtMzE3OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzozMi0zMTc6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGFydGljaXBhbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTg6MS0zMTg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjMyLTMxODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE5OjEtMzE5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTozMi0zMTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudE5vdGljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMDoxLTMyMDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6MzItMzIwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMToxLTMyMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6MzItMzIxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMjoxLTMyMjo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6MzItMzIyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5UmVwcmVzZW50YXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjM6MS0zMjM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjMyLTMyMzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9wZXJ0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjQ6MS0zMjQ6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjMyLTMyNDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm92ZW5hbmNlRW50aXR5Um9sZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNToxLTMyNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6MzItMzI1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI2OjEtMzI2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjozMi0zMjY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbGl0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjc6MS0zMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjMyLTMyNzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eUNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjg6MS0zMjg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjMyLTMyODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI5OjEtMzI5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTozMi0zMjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZUl0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMwOjEtMzMwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDozMi0zMzA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMxOjEtMzMxOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTozMi0zMzE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzI6MS0zMzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjMyLTMzMjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VWZXJzaW9uUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzM6MS0zMzM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjMyLTMzMzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcnJlZERvY3VtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM0OjEtMzM0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDozMi0zMzQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVsYXRlZEFydGlmYWN0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNToxLTMzNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6MzItMzM1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlbWl0dGFuY2VPdXRjb21lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM2OjEtMzM2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjozMi0zMzY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVwb3NpdG9yeVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzc6MS0zMzc6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjMyLTMzNzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM4OjEtMzM4OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODozMi0zMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM5OjEtMzM5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTozMi0zMzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MDoxLTM0MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6MzItMzQwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoRWxlbWVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDE6MS0zNDE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjMyLTM0MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQyOjEtMzQyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MjozMi0zNDI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hTdWJqZWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQzOjEtMzQzOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MzozMi0zNDM6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ0OjEtMzQ0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDozMi0zNDQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VWZXJzaW9uUG9saWN5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ1OjEtMzQ1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTozMi0zNDU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzcG9uc2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ2OjEtMzQ2OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NjozMi0zNDY6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ3OjEtMzQ3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NzozMi0zNDc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Umlza0Fzc2Vzc21lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDg6MS0zNDg6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjMyLTM0ODo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TUERYTGljZW5zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OToxLTM0OTo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6MzItMzQ5OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaENvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTA6MS0zNTA6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjMyLTM1MDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hFbnRyeU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTE6MS0zNTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjMyLTM1MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hNb2RpZmllckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTI6MS0zNTI6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjMyLTM1Mjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hQYXJhbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTM6MS0zNTM6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjMyLTM1Mzo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWN0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NDoxLTM1NDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6MzItMzU0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcXVlbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NToxLTM1NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6MzItMzU1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU2OjEtMzU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NjozMi0zNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NzoxLTM1Nzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6MzItMzU3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU4OjEtMzU4OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODozMi0zNTg6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xpY2luZ1J1bGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU5OjEtMzU5OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTozMi0zNTk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xvdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MDoxLTM2MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6MzItMzYwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNvcnREaXJlY3Rpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjE6MS0zNjE6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjMyLTM2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjI6MS0zNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjMyLTM2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MzoxLTM2Mzo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6MzItMzYzOjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NDoxLTM2NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6MzItMzY0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cmFuZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjU6MS0zNjU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjMyLTM2NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NjoxLTM2Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6MzItMzY2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcENvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY3OjEtMzY3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NzozMi0zNjc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2ODoxLTM2ODo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6MzItMzY4OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcElucHV0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OToxLTM2OTo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6MzItMzY5OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcE1vZGVsTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MDoxLTM3MDo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6MzItMzcwOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzcxOjEtMzcxOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTozMi0zNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzI6MS0zNzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjMyLTM3Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzM6MS0zNzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjMyLTM3Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdWJzY3JpcHRpb25DaGFubmVsVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NDoxLTM3NDo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6MzItMzc0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NToxLTM3NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6MzItMzc1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1cHBseURlbGl2ZXJ5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzc2OjEtMzc2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NjozMi0zNzY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NzoxLTM3Nzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6MzItMzc3OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3ODoxLTM3ODo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6MzItMzc4OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzk6MS0zNzk6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjMyLTM3OTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrUHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODA6MS0zODA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjMyLTM4MDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzgxOjEtMzgxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTozMi0zODE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydEFjdGlvblJlc3VsdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MjoxLTM4Mjo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6MzItMzgyOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODM6MS0zODM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjMyLTM4Mzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0UmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg0OjEtMzg0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDozMi0zODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NToxLTM4NTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6MzItMzg1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NjoxLTM4Njo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6MzItMzg2OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRyaWdnZXJUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg3OjEtMzg3OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NzozMi0zODc6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZURlcml2YXRpb25SdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg4OjEtMzg4OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODozMi0zODg6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OToxLTM4OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6MzItMzg5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVESUVudHJ5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MDoxLTM5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6MzItMzkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVuaXRzT2ZUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkxOjEtMzkxOjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozMi0zOTE6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkyOjEtMzkyOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MjozMi0zOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkzOjEtMzkzOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MzozMi0zOTM6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uQmFzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NDoxLTM5NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6MzItMzk0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvbkV5ZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTU6MS0zOTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjMyLTM5NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTY6MS0zOTY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjMyLTM5Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1YUGF0aFVzYWdlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NzoxLTM5Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6MzItMzk3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJhc2U2NEJpbmFyeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5ODoxLTM5ODo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0Jvb2xlYW4iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjMzLTM5ODozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1ib29sZWFuIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk5OjEtMzk5OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQ3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzAtMzk5OjMzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MS00MDA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MzQtNDAwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDAxOjEtNDAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGVjaW1hbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6MzMtNDAxOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRlY2ltYWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MS00MDI6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MzQtNDAyOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWluc3RhbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDM6MS00MDM6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlZ2VyIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzozMy00MDM6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNDoxLTQwNDo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6MzItNDA0OjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNToxLTQwNTo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1RpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjMwLTQwNTozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn10aW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA2OjEtNDA2OjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozMi00MDY6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dXJpIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA3OjEtNDA3OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NTAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzozMi00MDc6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9eGh0bWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/FHIRHelpers" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": "CMSTest-patient-2", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2028-9", + "display": "Asian" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2135-2", + "display": "Hispanic or Latino" + } + } + ] + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR", + "display": "Medical Record Number" + } + ] + }, + "system": "http://hospital.smarthealthit.org", + "value": "999999999" + } + ], + "name": [ + { + "family": "Dere", + "given": [ + "Ben" + ] + } + ], + "gender": "female", + "birthDate": "2000-03-01" + }, + "request": { + "method": "PUT", + "url": "Patient/CMSTest-patient-2" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "SDE", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/SDE", + "name": "SDE", + "title": "Library - Supplemental Data Elements", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "A Shared library encapsulating valuable common functions related to the use of Supplemental Data Elements used in Alphora dQM and QICore-based CQL artifacts.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Value set Ethnicity", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837" + }, + { + "type": "depends-on", + "display": "Value set ONC Administrative Sex", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + }, + { + "type": "depends-on", + "display": "Value set Race", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Coverage", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Coverage" + ], + "mustSupport": [ + "type", + "period", + "type.coding" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBTREUKCi8qQHVwZGF0ZTogQEBCVFIgMjAyMC0wMy0zMSAtPgpJbmNyZW1lbnRlZCB2ZXJzaW9uIHRvIDIuMC4wClVwZGF0ZWQgRkhJUiB2ZXJzaW9uIHRvIDQuMC4xCkBAQCovCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCnZhbHVlc2V0ICJFdGhuaWNpdHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3Jwp2YWx1ZXNldCAiT05DIEFkbWluaXN0cmF0aXZlIFNleCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEnCnZhbHVlc2V0ICJQYXllciI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS4zNTkxJwp2YWx1ZXNldCAiUmFjZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMC0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIxLTAxLTAxVDAwOjAwOjAwLjAwMCkKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eScKICAgICAgICAgIHJldHVybiBFeHRlbnNpb24uZXh0ZW5zaW9uCiAgICApKSBFCiAgICAgIHdoZXJlIEUudXJsID0gJ29tYkNhdGVnb3J5JwogICAgICAgIG9yIEUudXJsID0gJ2RldGFpbGVkJwogICAgICByZXR1cm4gRkhJUkhlbHBlcnMuVG9Db2RlKEUudmFsdWUpCgpkZWZpbmUgIlNERSBQYXllciI6CiAgZmxhdHRlbihbQ292ZXJhZ2U6IHR5cGUgaW4gIlBheWVyIl0gUGF5ZXIKICAgIHdoZXJlIFBheWVyLnBlcmlvZCBvdmVybGFwcyAiTWVhc3VyZW1lbnQgUGVyaW9kIgogICAgICByZXR1cm4gKAogICAgICAgIFBheWVyLnR5cGUuY29kaW5nIGMKICAgICAgICAgIHJldHVybiBGSElSSGVscGVycy5Ub0NvZGUoYykKICAgICAgKQogICkKCmRlZmluZSAiU0RFIFJhY2UiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UnCiAgICAgICAgICByZXR1cm4gRXh0ZW5zaW9uLmV4dGVuc2lvbgogICAgKSkgRQogICAgICB3aGVyZSBFLnVybCA9ICdvbWJDYXRlZ29yeScKICAgICAgICBvciBFLnVybCA9ICdkZXRhaWxlZCcKICAgICAgcmV0dXJuIEZISVJIZWxwZXJzLlRvQ29kZShFLnZhbHVlKQoKZGVmaW5lICJTREUgU2V4IjoKICBjYXNlCiAgICAgIHdoZW4gUGF0aWVudC5nZW5kZXIgPSAnbWFsZScgdGhlbiBDb2RlIHsgY29kZTogJ00nLCBzeXN0ZW06ICdodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyJywgZGlzcGxheTogJ01hbGUnIH0KICAgICAgd2hlbiBQYXRpZW50LmdlbmRlciA9ICdmZW1hbGUnIHRoZW4gQ29kZSB7IGNvZGU6ICdGJywgc3lzdGVtOiAnaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlcicsIGRpc3BsYXk6ICdGZW1hbGUnIH0KICAgICAgZWxzZSBudWxsCiAgICBlbmQK" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iU0RFIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iODoxLTg6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9InVwZGF0ZSIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgIDwvZGVmPgogICA8L3VzaW5ncz4KICAgPGluY2x1ZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOjEtMTA6NDEiIGxvY2FsSWRlbnRpZmllcj0iRkhJUkhlbHBlcnMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIi8+CiAgIDwvaW5jbHVkZXM+CiAgIDxwYXJhbWV0ZXJzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3OjEtMTc6OTkiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxkZWZhdWx0IGxvY2F0b3I9IjE3OjQwLTE3Ojk5IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE3OjQ5LTE3OjcyIiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE3Ojc1LTE3Ojk4IiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2RlZmF1bHQ+CiAgICAgIDwvZGVmPgogICA8L3BhcmFtZXRlcnM+CiAgIDx2YWx1ZVNldHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjo4NyIgbmFtZT0iRXRobmljaXR5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo5NyIgbmFtZT0iT05DIEFkbWluaXN0cmF0aXZlIFNleCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0OjEtMTQ6ODQiIG5hbWU9IlBheWVyIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1OjEtMTU6ODIiIG5hbWU9IlJhY2UiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OjEtMTg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjEtMTg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDoxLTI4OjQwIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yODo0MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTozLTI1OjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yNTo2IiB4c2k6dHlwZT0iRmxhdHRlbiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjEyLTI1OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI6Ny0yMjozMyIgYWxpYXM9IkV4dGVuc2lvbiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyOjctMjI6MjMiIHBhdGg9ImV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjM6OS0yMzo5NyIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MTUtMjM6MjciIHBhdGg9InVybCIgc2NvcGU9IkV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MzEtMjM6OTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNDoxMS0yNDozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0OjE4LTI0OjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjY6Ny0yNzoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6MTMtMjY6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjEzLTI2OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyMS0yNjozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjEyLTI3OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoxMi0yNzoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjc6MjAtMjc6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI4OjctMjg6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODoxNC0yODo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OjMzLTI4OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zNzozIiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTozLTM3OjMiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzE6MTAtMzc6MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMToxMS0zMTo0MyIgYWxpYXM9IlBheWVyIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE6MTEtMzE6MzciIGRhdGFUeXBlPSJmaGlyOkNvdmVyYWdlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ292ZXJhZ2UiIGNvZGVQcm9wZXJ0eT0idHlwZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzE6MzAtMzE6MzYiIG5hbWU9IlBheWVyIiBwcmVzZXJ2ZT0idHJ1ZSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMjo1LTMyOjUyIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMyOjExLTMyOjIyIiBwYXRoPSJwZXJpb2QiIHNjb3BlPSJQYXllciIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzI6MzMtMzI6NTIiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMzOjctMzY6NyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzOjE0LTM2OjciIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ6OS0zNDoyNyIgYWxpYXM9ImMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDo5LTM0OjI1IiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0idHlwZSIgc2NvcGU9IlBheWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIzNToxMS0zNTozOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1OjE4LTM1OjM4IiBuYW1lPSJUb0NvZGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzU6MzciIG5hbWU9ImMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzk6MS00Nzo0MCIgbmFtZT0iU0RFIFJhY2UiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ3OjQwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwOjMtNDQ6OCIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ0OjYiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTItNDQ6NSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MTo3LTQxOjMzIiBhbGlhcz0iRXh0ZW5zaW9uIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDE6Ny00MToyMyIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSI0Mjo5LTQyOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxNS00MjoyNyIgcGF0aD0idXJsIiBzY29wZT0iRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjozMS00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvdXMvY29yZS9TdHJ1Y3R1cmVEZWZpbml0aW9uL3VzLWNvcmUtcmFjZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSI0MzoxMS00MzozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQzOjE4LTQzOjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iNDU6Ny00NjoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTMtNDU6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToyMS00NTozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ2OjEyLTQ2OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NjoxMi00NjoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDY6MjAtNDY6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjQ3OjctNDc6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NzoxNC00Nzo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ3OjMzLTQ3OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDk6MS01NDo3IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTA6My01NDo3IiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1MTo3LTUxOjEzMCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUxOjEyLTUxOjM0IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MToxMi01MToyNSIgcGF0aD0iZ2VuZGVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjEyLTUxOjE4IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTE6MjktNTE6MzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUxOjQxLTUxOjEzMCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6NTQtNTE6NTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJNIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1MTo2Ny01MToxMTEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6MTIzLTUxOjEyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ik1hbGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTI6Ny01MjoxMzQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MjoxMi01MjozNiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTItNTI6MjUiIHBhdGg9ImdlbmRlciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MjoxMi01MjoxOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjI5LTUyOjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmVtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUyOjQzLTUyOjEzNCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6NTYtNTI6NTgiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1Mjo2OS01MjoxMTMiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6MTI1LTUyOjEzMiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZlbWFsZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxMi01MzoxNSIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" + }, + { + "contentType": "application/elm+json", + "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiU0RFIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVwZGF0ZSIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6NDEiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6OTkiLAogICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZGVmYXVsdCIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo0MC0xNzo5OSIsCiAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NDktMTc6NzIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NzUtMTc6OTgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAidmFsdWVTZXRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiRXRobmljaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo5NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJPTkMgQWRtaW5pc3RyYXRpdmUgU2V4IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDo4NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQYXllciIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlJhY2UiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExNDIyMi40LjExLjgzNiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI4OjQwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yODo0MCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjgiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjEyLTI1OjUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjoyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzo5LTIzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjE1LTIzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6MzEtMjM6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci91cy9jb3JlL1N0cnVjdHVyZURlZmluaXRpb24vdXMtY29yZS1ldGhuaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MTEtMjQ6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDoxOC0yNDozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNzoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MjEtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjIwLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODo3LTI4OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MTQtMjg6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODozMy0yODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zNzozIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTozLTM3OjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEwLTM3OjMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6NDMiLAogICAgICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db3ZlcmFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogInR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjMwLTMxOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlcnZlIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjo1LTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MTEtMzI6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMzLTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjctMzY6NyIsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjE0LTM2OjciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6OS0zNDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjktMzQ6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTEtMzU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxOC0zNTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS00Nzo0MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDozLTQ3OjQwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6OCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGbGF0dGVuIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTItNDQ6NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjktNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTUtNDI6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjozMS00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MTEtNDM6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxOC00MzozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6Ny00NjoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIwLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo3LTQ3OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTQtNDc6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NzozMy00NzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS01NDo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6My01NDo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo3LTUxOjEzMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjM0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTItNTE6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MToyOS01MTozNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0MS01MToxMzAiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NTQtNTE6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2Ny01MToxMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTIzLTUxOjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6Ny01MjoxMzQiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjozNiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJnZW5kZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEyLTUyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MjktNTI6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0My01MjoxMzQiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NTYtNTI6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo2OS01MjoxMTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTI1LTUyOjEzMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGZW1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTItNTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/SDE" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.526.3.378", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareablevalueset", + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-publishablevalueset", + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "shareable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "narrative" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-inclusion", + "valueString": "Includes concepts that represent a diagnosis of pregnancy." + }, + { + "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-exclusion", + "valueString": "No exclusions." + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "executable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "executable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning", + "valueString": "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion." + } + ], + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378", + "version": "20210218", + "title": "Pregnancy", + "status": "active", + "experimental": false, + "publisher": "American Heart Association, Inc.", + "description": "The purpose of this value set is to represent concepts for diagnoses of pregnancy.", + "purpose": "This value set may use a model element related to Diagnosis.", + "expansion": { + "timestamp": "2023-01-02T15:36:29-07:00", + "contains": [ + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "10231000132102", + "display": "In-vitro fertilization pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "102872000", + "display": "Pregnancy on oral contraceptive (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "102875003", + "display": "Surrogate pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "11082009", + "display": "Abnormal pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "127364007", + "display": "Primigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "134781000119106", + "display": "High risk pregnancy due to recurrent miscarriage (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "14080002", + "display": "Illegitimate pregnancy, life event (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "14418008", + "display": "Precocious pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "16356006", + "display": "Multiple pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169561007", + "display": "Pregnant - blood test confirms (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169562000", + "display": "Pregnant - vaginal examination confirms (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169563005", + "display": "Pregnant - on history (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169564004", + "display": "Pregnant - on abdominal palpation (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169565003", + "display": "Pregnant - planned (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169566002", + "display": "Pregnancy unplanned but wanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169567006", + "display": "Pregnancy unplanned and unwanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169568001", + "display": "Unplanned pregnancy unknown if child is wanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "199064003", + "display": "Post-term pregnancy - not delivered (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "199306007", + "display": "Continuing pregnancy after abortion of one fetus or more (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "22281000119101", + "display": "Post-term pregnancy of 40 to 42 weeks (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237233002", + "display": "Concealed pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237238006", + "display": "Pregnancy with uncertain dates (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237239003", + "display": "Low risk pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237240001", + "display": "Teenage pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237241002", + "display": "Viable pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237244005", + "display": "Single pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "248985009", + "display": "Presentation of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "276367008", + "display": "Wanted pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "281307002", + "display": "Uncertain viability of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "314204000", + "display": "Early stage of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "35381000119101", + "display": "Quadruplet pregnancy with loss of one or more fetuses (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "36801000119105", + "display": "Continuing triplet pregnancy after spontaneous abortion of one or more fetuses (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "38720006", + "display": "Septuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "41587001", + "display": "Third trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "41991004", + "display": "Angiectasis pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "429187001", + "display": "Continuing pregnancy after intrauterine death of twin fetus (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "43990006", + "display": "Sextuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "442478007", + "display": "Multiple pregnancy involving intrauterine pregnancy and tubal pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "444661007", + "display": "High risk pregnancy due to history of preterm labor (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "45307008", + "display": "Extrachorial pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "457811000124103", + "display": "Normal pregnancy in primigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "457821000124106", + "display": "Normal pregnancy in multigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459167000", + "display": "Monochorionic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459169002", + "display": "Monochorionic diamniotic twin pregnancy with similar amniotic fluid volumes (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459170001", + "display": "Monochorionic diamniotic twin pregnancy with dissimilar amniotic fluid volumes (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "47200007", + "display": "High risk pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "472321009", + "display": "Continuing pregnancy after intrauterine death of one twin with intrauterine retention of dead twin (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "57630001", + "display": "First trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "58532003", + "display": "Unwanted pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "59466002", + "display": "Second trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "60810003", + "display": "Quadruplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "64254006", + "display": "Triplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "65147003", + "display": "Twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "65727000", + "display": "Intrauterine pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "713575004", + "display": "Dizygotic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "713576003", + "display": "Monozygotic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "72892002", + "display": "Normal pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "77386006", + "display": "Pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "80997009", + "display": "Quintuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "83074005", + "display": "Unplanned pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "87527008", + "display": "Term pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "90968009", + "display": "Prolonged pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "9279009", + "display": "Extra-amniotic pregnancy (finding)" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.00", + "display": "Supervision of pregnancy with history of infertility, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.01", + "display": "Supervision of pregnancy with history of infertility, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.02", + "display": "Supervision of pregnancy with history of infertility, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.03", + "display": "Supervision of pregnancy with history of infertility, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.10", + "display": "Supervision of pregnancy with history of ectopic pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.11", + "display": "Supervision of pregnancy with history of ectopic pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.12", + "display": "Supervision of pregnancy with history of ectopic pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.13", + "display": "Supervision of pregnancy with history of ectopic pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.211", + "display": "Supervision of pregnancy with history of pre-term labor, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.212", + "display": "Supervision of pregnancy with history of pre-term labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.213", + "display": "Supervision of pregnancy with history of pre-term labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.219", + "display": "Supervision of pregnancy with history of pre-term labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.291", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.292", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.293", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.299", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.30", + "display": "Supervision of pregnancy with insufficient antenatal care, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.31", + "display": "Supervision of pregnancy with insufficient antenatal care, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.32", + "display": "Supervision of pregnancy with insufficient antenatal care, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.33", + "display": "Supervision of pregnancy with insufficient antenatal care, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.40", + "display": "Supervision of pregnancy with grand multiparity, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.41", + "display": "Supervision of pregnancy with grand multiparity, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.42", + "display": "Supervision of pregnancy with grand multiparity, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.43", + "display": "Supervision of pregnancy with grand multiparity, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.511", + "display": "Supervision of elderly primigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.512", + "display": "Supervision of elderly primigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.513", + "display": "Supervision of elderly primigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.519", + "display": "Supervision of elderly primigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.521", + "display": "Supervision of elderly multigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.522", + "display": "Supervision of elderly multigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.523", + "display": "Supervision of elderly multigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.529", + "display": "Supervision of elderly multigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.611", + "display": "Supervision of young primigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.612", + "display": "Supervision of young primigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.613", + "display": "Supervision of young primigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.619", + "display": "Supervision of young primigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.621", + "display": "Supervision of young multigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.622", + "display": "Supervision of young multigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.623", + "display": "Supervision of young multigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.629", + "display": "Supervision of young multigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.70", + "display": "Supervision of high risk pregnancy due to social problems, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.71", + "display": "Supervision of high risk pregnancy due to social problems, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.72", + "display": "Supervision of high risk pregnancy due to social problems, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.73", + "display": "Supervision of high risk pregnancy due to social problems, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.811", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.812", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.813", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.819", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.821", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.822", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.823", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.829", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.891", + "display": "Supervision of other high risk pregnancies, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.892", + "display": "Supervision of other high risk pregnancies, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.893", + "display": "Supervision of other high risk pregnancies, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.899", + "display": "Supervision of other high risk pregnancies, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.90", + "display": "Supervision of high risk pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.91", + "display": "Supervision of high risk pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.92", + "display": "Supervision of high risk pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.93", + "display": "Supervision of high risk pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A0", + "display": "Supervision of pregnancy with history of molar pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A1", + "display": "Supervision of pregnancy with history of molar pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A2", + "display": "Supervision of pregnancy with history of molar pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A3", + "display": "Supervision of pregnancy with history of molar pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.011", + "display": "Pre-existing essential hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.012", + "display": "Pre-existing essential hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.013", + "display": "Pre-existing essential hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.019", + "display": "Pre-existing essential hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.111", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.112", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.113", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.119", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.211", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.212", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.213", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.219", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.311", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.312", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.313", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.319", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.411", + "display": "Pre-existing secondary hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.412", + "display": "Pre-existing secondary hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.413", + "display": "Pre-existing secondary hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.419", + "display": "Pre-existing secondary hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.911", + "display": "Unspecified pre-existing hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.912", + "display": "Unspecified pre-existing hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.913", + "display": "Unspecified pre-existing hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.919", + "display": "Unspecified pre-existing hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.1", + "display": "Pre-existing hypertension with pre-eclampsia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.2", + "display": "Pre-existing hypertension with pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.3", + "display": "Pre-existing hypertension with pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.9", + "display": "Pre-existing hypertension with pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.00", + "display": "Gestational edema, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.01", + "display": "Gestational edema, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.02", + "display": "Gestational edema, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.03", + "display": "Gestational edema, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.10", + "display": "Gestational proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.11", + "display": "Gestational proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.12", + "display": "Gestational proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.13", + "display": "Gestational proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.20", + "display": "Gestational edema with proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.21", + "display": "Gestational edema with proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.22", + "display": "Gestational edema with proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.23", + "display": "Gestational edema with proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.1", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.2", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.3", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.9", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.00", + "display": "Mild to moderate pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.02", + "display": "Mild to moderate pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.03", + "display": "Mild to moderate pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.10", + "display": "Severe pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.12", + "display": "Severe pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.13", + "display": "Severe pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.20", + "display": "HELLP syndrome (HELLP), unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.22", + "display": "HELLP syndrome (HELLP), second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.23", + "display": "HELLP syndrome (HELLP), third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.90", + "display": "Unspecified pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.92", + "display": "Unspecified pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.93", + "display": "Unspecified pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.00", + "display": "Eclampsia complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.02", + "display": "Eclampsia complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.03", + "display": "Eclampsia complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.1", + "display": "Unspecified maternal hypertension, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.2", + "display": "Unspecified maternal hypertension, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.3", + "display": "Unspecified maternal hypertension, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.9", + "display": "Unspecified maternal hypertension, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.0", + "display": "Threatened abortion" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.8", + "display": "Other hemorrhage in early pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.9", + "display": "Hemorrhage in early pregnancy, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.0", + "display": "Mild hyperemesis gravidarum" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.1", + "display": "Hyperemesis gravidarum with metabolic disturbance" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.2", + "display": "Late vomiting of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.8", + "display": "Other vomiting complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.9", + "display": "Vomiting of pregnancy, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.00", + "display": "Varicose veins of lower extremity in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.01", + "display": "Varicose veins of lower extremity in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.02", + "display": "Varicose veins of lower extremity in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.03", + "display": "Varicose veins of lower extremity in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.10", + "display": "Genital varices in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.11", + "display": "Genital varices in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.12", + "display": "Genital varices in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.13", + "display": "Genital varices in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.20", + "display": "Superficial thrombophlebitis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.21", + "display": "Superficial thrombophlebitis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.22", + "display": "Superficial thrombophlebitis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.23", + "display": "Superficial thrombophlebitis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.30", + "display": "Deep phlebothrombosis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.31", + "display": "Deep phlebothrombosis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.32", + "display": "Deep phlebothrombosis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.33", + "display": "Deep phlebothrombosis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.40", + "display": "Hemorrhoids in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.41", + "display": "Hemorrhoids in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.42", + "display": "Hemorrhoids in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.43", + "display": "Hemorrhoids in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.50", + "display": "Cerebral venous thrombosis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.51", + "display": "Cerebral venous thrombosis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.52", + "display": "Cerebral venous thrombosis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.53", + "display": "Cerebral venous thrombosis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X1", + "display": "Other venous complications in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X2", + "display": "Other venous complications in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X3", + "display": "Other venous complications in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X9", + "display": "Other venous complications in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.90", + "display": "Venous complication in pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.91", + "display": "Venous complication in pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.92", + "display": "Venous complication in pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.93", + "display": "Venous complication in pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.00", + "display": "Infections of kidney in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.01", + "display": "Infections of kidney in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.02", + "display": "Infections of kidney in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.03", + "display": "Infections of kidney in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.10", + "display": "Infections of bladder in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.11", + "display": "Infections of bladder in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.12", + "display": "Infections of bladder in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.13", + "display": "Infections of bladder in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.20", + "display": "Infections of urethra in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.21", + "display": "Infections of urethra in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.22", + "display": "Infections of urethra in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.23", + "display": "Infections of urethra in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.30", + "display": "Infections of other parts of urinary tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.31", + "display": "Infections of other parts of urinary tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.32", + "display": "Infections of other parts of urinary tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.33", + "display": "Infections of other parts of urinary tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.40", + "display": "Unspecified infection of urinary tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.41", + "display": "Unspecified infection of urinary tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.42", + "display": "Unspecified infection of urinary tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.43", + "display": "Unspecified infection of urinary tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.511", + "display": "Infections of cervix in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.512", + "display": "Infections of cervix in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.513", + "display": "Infections of cervix in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.519", + "display": "Infections of cervix in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.521", + "display": "Salpingo-oophoritis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.522", + "display": "Salpingo-oophoritis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.523", + "display": "Salpingo-oophoritis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.529", + "display": "Salpingo-oophoritis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.591", + "display": "Infection of other part of genital tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.592", + "display": "Infection of other part of genital tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.593", + "display": "Infection of other part of genital tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.599", + "display": "Infection of other part of genital tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.90", + "display": "Unspecified genitourinary tract infection in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.91", + "display": "Unspecified genitourinary tract infection in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.92", + "display": "Unspecified genitourinary tract infection in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.93", + "display": "Unspecified genitourinary tract infection in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.011", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.012", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.013", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.019", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.111", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.112", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.113", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.119", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.311", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.312", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.313", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.319", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.410", + "display": "Gestational diabetes mellitus in pregnancy, diet controlled" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.414", + "display": "Gestational diabetes mellitus in pregnancy, insulin controlled" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.415", + "display": "Gestational diabetes mellitus in pregnancy, controlled by oral hypoglycemic drugs" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.419", + "display": "Gestational diabetes mellitus in pregnancy, unspecified control" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.811", + "display": "Other pre-existing diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.812", + "display": "Other pre-existing diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.813", + "display": "Other pre-existing diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.819", + "display": "Other pre-existing diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.911", + "display": "Unspecified diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.912", + "display": "Unspecified diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.913", + "display": "Unspecified diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.919", + "display": "Unspecified diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.10", + "display": "Malnutrition in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.11", + "display": "Malnutrition in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.12", + "display": "Malnutrition in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.13", + "display": "Malnutrition in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.00", + "display": "Excessive weight gain in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.01", + "display": "Excessive weight gain in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.02", + "display": "Excessive weight gain in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.03", + "display": "Excessive weight gain in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.10", + "display": "Low weight gain in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.11", + "display": "Low weight gain in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.12", + "display": "Low weight gain in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.13", + "display": "Low weight gain in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.20", + "display": "Pregnancy care for patient with recurrent pregnancy loss, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.21", + "display": "Pregnancy care for patient with recurrent pregnancy loss, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.22", + "display": "Pregnancy care for patient with recurrent pregnancy loss, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.23", + "display": "Pregnancy care for patient with recurrent pregnancy loss, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.30", + "display": "Retained intrauterine contraceptive device in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.31", + "display": "Retained intrauterine contraceptive device in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.32", + "display": "Retained intrauterine contraceptive device in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.33", + "display": "Retained intrauterine contraceptive device in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.40", + "display": "Herpes gestationis, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.41", + "display": "Herpes gestationis, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.42", + "display": "Herpes gestationis, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.43", + "display": "Herpes gestationis, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.50", + "display": "Maternal hypotension syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.51", + "display": "Maternal hypotension syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.52", + "display": "Maternal hypotension syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.53", + "display": "Maternal hypotension syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.611", + "display": "Liver and biliary tract disorders in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.612", + "display": "Liver and biliary tract disorders in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.613", + "display": "Liver and biliary tract disorders in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.619", + "display": "Liver and biliary tract disorders in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.711", + "display": "Subluxation of symphysis (pubis) in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.712", + "display": "Subluxation of symphysis (pubis) in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.713", + "display": "Subluxation of symphysis (pubis) in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.719", + "display": "Subluxation of symphysis (pubis) in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.811", + "display": "Pregnancy related exhaustion and fatigue, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.812", + "display": "Pregnancy related exhaustion and fatigue, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.813", + "display": "Pregnancy related exhaustion and fatigue, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.819", + "display": "Pregnancy related exhaustion and fatigue, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.821", + "display": "Pregnancy related peripheral neuritis, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.822", + "display": "Pregnancy related peripheral neuritis, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.823", + "display": "Pregnancy related peripheral neuritis, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.829", + "display": "Pregnancy related peripheral neuritis, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.831", + "display": "Pregnancy related renal disease, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.832", + "display": "Pregnancy related renal disease, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.833", + "display": "Pregnancy related renal disease, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.839", + "display": "Pregnancy related renal disease, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.841", + "display": "Uterine size-date discrepancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.842", + "display": "Uterine size-date discrepancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.843", + "display": "Uterine size-date discrepancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.849", + "display": "Uterine size-date discrepancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.851", + "display": "Spotting complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.852", + "display": "Spotting complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.853", + "display": "Spotting complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.859", + "display": "Spotting complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.86", + "display": "Pruritic urticarial papules and plaques of pregnancy (PUPPP)" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.872", + "display": "Cervical shortening, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.873", + "display": "Cervical shortening, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.879", + "display": "Cervical shortening, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.891", + "display": "Other specified pregnancy related conditions, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.892", + "display": "Other specified pregnancy related conditions, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.893", + "display": "Other specified pregnancy related conditions, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.899", + "display": "Other specified pregnancy related conditions, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.90", + "display": "Pregnancy related conditions, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.91", + "display": "Pregnancy related conditions, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.92", + "display": "Pregnancy related conditions, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.93", + "display": "Pregnancy related conditions, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.0", + "display": "Abnormal hematological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.1", + "display": "Abnormal biochemical finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.2", + "display": "Abnormal cytological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.3", + "display": "Abnormal ultrasonic finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.4", + "display": "Abnormal radiological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.5", + "display": "Abnormal chromosomal and genetic finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.8", + "display": "Other abnormal findings on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.9", + "display": "Unspecified abnormal findings on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.011", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.012", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.013", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.019", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.021", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.022", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.023", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.029", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.091", + "display": "Other pulmonary complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.092", + "display": "Other pulmonary complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.093", + "display": "Other pulmonary complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.099", + "display": "Other pulmonary complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.111", + "display": "Cardiac arrest due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.112", + "display": "Cardiac arrest due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.113", + "display": "Cardiac arrest due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.119", + "display": "Cardiac arrest due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.121", + "display": "Cardiac failure due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.122", + "display": "Cardiac failure due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.123", + "display": "Cardiac failure due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.129", + "display": "Cardiac failure due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.191", + "display": "Other cardiac complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.192", + "display": "Other cardiac complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.193", + "display": "Other cardiac complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.199", + "display": "Other cardiac complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.211", + "display": "Cerebral anoxia due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.212", + "display": "Cerebral anoxia due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.213", + "display": "Cerebral anoxia due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.219", + "display": "Cerebral anoxia due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.291", + "display": "Other central nervous system complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.292", + "display": "Other central nervous system complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.293", + "display": "Other central nervous system complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.299", + "display": "Other central nervous system complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X1", + "display": "Toxic reaction to local anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X2", + "display": "Toxic reaction to local anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X3", + "display": "Toxic reaction to local anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X9", + "display": "Toxic reaction to local anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.40", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.41", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.42", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.43", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X1", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X2", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X3", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X9", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.60", + "display": "Failed or difficult intubation for anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.61", + "display": "Failed or difficult intubation for anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.62", + "display": "Failed or difficult intubation for anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.63", + "display": "Failed or difficult intubation for anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X1", + "display": "Other complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X2", + "display": "Other complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X3", + "display": "Other complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X9", + "display": "Other complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.90", + "display": "Unspecified complication of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.91", + "display": "Unspecified complication of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.92", + "display": "Unspecified complication of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.93", + "display": "Unspecified complication of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.001", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.002", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.003", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.009", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.011", + "display": "Twin pregnancy, monochorionic/monoamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.012", + "display": "Twin pregnancy, monochorionic/monoamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.013", + "display": "Twin pregnancy, monochorionic/monoamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.019", + "display": "Twin pregnancy, monochorionic/monoamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.021", + "display": "Conjoined twin pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.022", + "display": "Conjoined twin pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.023", + "display": "Conjoined twin pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.029", + "display": "Conjoined twin pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.031", + "display": "Twin pregnancy, monochorionic/diamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.032", + "display": "Twin pregnancy, monochorionic/diamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.033", + "display": "Twin pregnancy, monochorionic/diamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.039", + "display": "Twin pregnancy, monochorionic/diamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.041", + "display": "Twin pregnancy, dichorionic/diamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.042", + "display": "Twin pregnancy, dichorionic/diamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.043", + "display": "Twin pregnancy, dichorionic/diamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.049", + "display": "Twin pregnancy, dichorionic/diamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.091", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.092", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.093", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.099", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.101", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.102", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.103", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.109", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.111", + "display": "Triplet pregnancy with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.112", + "display": "Triplet pregnancy with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.113", + "display": "Triplet pregnancy with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.119", + "display": "Triplet pregnancy with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.121", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.122", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.123", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.129", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.131", + "display": "Triplet pregnancy, trichorionic/triamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.132", + "display": "Triplet pregnancy, trichorionic/triamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.133", + "display": "Triplet pregnancy, trichorionic/triamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.139", + "display": "Triplet pregnancy, trichorionic/triamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.191", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.192", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.193", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.199", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.201", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.202", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.203", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.209", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.211", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.212", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.213", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.219", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.221", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.222", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.223", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.229", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.231", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.232", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.233", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.239", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.291", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.292", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.293", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.299", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.801", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.802", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.803", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.809", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.811", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.812", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.813", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.819", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.821", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.822", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.823", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.829", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.831", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.832", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.833", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.839", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.891", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.892", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.893", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.899", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.90", + "display": "Multiple gestation, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.91", + "display": "Multiple gestation, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.92", + "display": "Multiple gestation, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.93", + "display": "Multiple gestation, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X0", + "display": "Papyraceous fetus, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X1", + "display": "Papyraceous fetus, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X2", + "display": "Papyraceous fetus, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X3", + "display": "Papyraceous fetus, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X4", + "display": "Papyraceous fetus, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X5", + "display": "Papyraceous fetus, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X9", + "display": "Papyraceous fetus, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X0", + "display": "Papyraceous fetus, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X1", + "display": "Papyraceous fetus, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X2", + "display": "Papyraceous fetus, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X3", + "display": "Papyraceous fetus, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X4", + "display": "Papyraceous fetus, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X5", + "display": "Papyraceous fetus, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X9", + "display": "Papyraceous fetus, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X0", + "display": "Papyraceous fetus, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X1", + "display": "Papyraceous fetus, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X2", + "display": "Papyraceous fetus, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X3", + "display": "Papyraceous fetus, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X4", + "display": "Papyraceous fetus, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X5", + "display": "Papyraceous fetus, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X9", + "display": "Papyraceous fetus, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X0", + "display": "Papyraceous fetus, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X1", + "display": "Papyraceous fetus, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X2", + "display": "Papyraceous fetus, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X3", + "display": "Papyraceous fetus, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X4", + "display": "Papyraceous fetus, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X5", + "display": "Papyraceous fetus, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X9", + "display": "Papyraceous fetus, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X10", + "display": "Other complications specific to multiple gestation, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X11", + "display": "Other complications specific to multiple gestation, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X12", + "display": "Other complications specific to multiple gestation, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X13", + "display": "Other complications specific to multiple gestation, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X14", + "display": "Other complications specific to multiple gestation, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X15", + "display": "Other complications specific to multiple gestation, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X19", + "display": "Other complications specific to multiple gestation, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X20", + "display": "Other complications specific to multiple gestation, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X21", + "display": "Other complications specific to multiple gestation, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X22", + "display": "Other complications specific to multiple gestation, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X23", + "display": "Other complications specific to multiple gestation, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X24", + "display": "Other complications specific to multiple gestation, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X25", + "display": "Other complications specific to multiple gestation, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X29", + "display": "Other complications specific to multiple gestation, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X30", + "display": "Other complications specific to multiple gestation, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X31", + "display": "Other complications specific to multiple gestation, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X32", + "display": "Other complications specific to multiple gestation, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X33", + "display": "Other complications specific to multiple gestation, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X34", + "display": "Other complications specific to multiple gestation, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X35", + "display": "Other complications specific to multiple gestation, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X39", + "display": "Other complications specific to multiple gestation, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X90", + "display": "Other complications specific to multiple gestation, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X91", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X92", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X93", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X94", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X95", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X99", + "display": "Other complications specific to multiple gestation, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX0", + "display": "Maternal care for unstable lie, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX1", + "display": "Maternal care for unstable lie, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX2", + "display": "Maternal care for unstable lie, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX3", + "display": "Maternal care for unstable lie, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX4", + "display": "Maternal care for unstable lie, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX5", + "display": "Maternal care for unstable lie, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX9", + "display": "Maternal care for unstable lie, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX0", + "display": "Maternal care for breech presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX1", + "display": "Maternal care for breech presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX2", + "display": "Maternal care for breech presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX3", + "display": "Maternal care for breech presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX4", + "display": "Maternal care for breech presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX5", + "display": "Maternal care for breech presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX9", + "display": "Maternal care for breech presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX0", + "display": "Maternal care for transverse and oblique lie, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX1", + "display": "Maternal care for transverse and oblique lie, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX2", + "display": "Maternal care for transverse and oblique lie, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX3", + "display": "Maternal care for transverse and oblique lie, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX4", + "display": "Maternal care for transverse and oblique lie, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX5", + "display": "Maternal care for transverse and oblique lie, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX9", + "display": "Maternal care for transverse and oblique lie, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX0", + "display": "Maternal care for face, brow and chin presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX1", + "display": "Maternal care for face, brow and chin presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX2", + "display": "Maternal care for face, brow and chin presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX3", + "display": "Maternal care for face, brow and chin presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX4", + "display": "Maternal care for face, brow and chin presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX5", + "display": "Maternal care for face, brow and chin presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX9", + "display": "Maternal care for face, brow and chin presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX0", + "display": "Maternal care for high head at term, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX1", + "display": "Maternal care for high head at term, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX2", + "display": "Maternal care for high head at term, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX3", + "display": "Maternal care for high head at term, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX4", + "display": "Maternal care for high head at term, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX5", + "display": "Maternal care for high head at term, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX9", + "display": "Maternal care for high head at term, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX0", + "display": "Maternal care for compound presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX1", + "display": "Maternal care for compound presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX2", + "display": "Maternal care for compound presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX3", + "display": "Maternal care for compound presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX4", + "display": "Maternal care for compound presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX5", + "display": "Maternal care for compound presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX9", + "display": "Maternal care for compound presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX0", + "display": "Maternal care for other malpresentation of fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX1", + "display": "Maternal care for other malpresentation of fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX2", + "display": "Maternal care for other malpresentation of fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX3", + "display": "Maternal care for other malpresentation of fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX4", + "display": "Maternal care for other malpresentation of fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX5", + "display": "Maternal care for other malpresentation of fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX9", + "display": "Maternal care for other malpresentation of fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX0", + "display": "Maternal care for malpresentation of fetus, unspecified, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX1", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX2", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX3", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX4", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX5", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX9", + "display": "Maternal care for malpresentation of fetus, unspecified, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.0", + "display": "Maternal care for disproportion due to deformity of maternal pelvic bones" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.1", + "display": "Maternal care for disproportion due to generally contracted pelvis" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.2", + "display": "Maternal care for disproportion due to inlet contraction of pelvis" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX0", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX1", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX2", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX3", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX4", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX5", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX9", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX0", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX1", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX2", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX3", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX4", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX5", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX9", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX0", + "display": "Maternal care for disproportion due to unusually large fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX1", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX2", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX3", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX4", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX5", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX9", + "display": "Maternal care for disproportion due to unusually large fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX0", + "display": "Maternal care for disproportion due to hydrocephalic fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX1", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX2", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX3", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX4", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX5", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX9", + "display": "Maternal care for disproportion due to hydrocephalic fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX0", + "display": "Maternal care for disproportion due to other fetal deformities, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX1", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX2", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX3", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX4", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX5", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX9", + "display": "Maternal care for disproportion due to other fetal deformities, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.8", + "display": "Maternal care for disproportion of other origin" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.9", + "display": "Maternal care for disproportion, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.00", + "display": "Maternal care for unspecified congenital malformation of uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.01", + "display": "Maternal care for unspecified congenital malformation of uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.02", + "display": "Maternal care for unspecified congenital malformation of uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.03", + "display": "Maternal care for unspecified congenital malformation of uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.10", + "display": "Maternal care for benign tumor of corpus uteri, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.11", + "display": "Maternal care for benign tumor of corpus uteri, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.12", + "display": "Maternal care for benign tumor of corpus uteri, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.13", + "display": "Maternal care for benign tumor of corpus uteri, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.211", + "display": "Maternal care for low transverse scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.212", + "display": "Maternal care for vertical scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.219", + "display": "Maternal care for unspecified type scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.29", + "display": "Maternal care due to uterine scar from other previous surgery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.30", + "display": "Maternal care for cervical incompetence, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.31", + "display": "Maternal care for cervical incompetence, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.32", + "display": "Maternal care for cervical incompetence, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.33", + "display": "Maternal care for cervical incompetence, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.40", + "display": "Maternal care for other abnormalities of cervix, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.41", + "display": "Maternal care for other abnormalities of cervix, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.42", + "display": "Maternal care for other abnormalities of cervix, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.43", + "display": "Maternal care for other abnormalities of cervix, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.511", + "display": "Maternal care for incarceration of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.512", + "display": "Maternal care for incarceration of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.513", + "display": "Maternal care for incarceration of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.519", + "display": "Maternal care for incarceration of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.521", + "display": "Maternal care for prolapse of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.522", + "display": "Maternal care for prolapse of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.523", + "display": "Maternal care for prolapse of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.529", + "display": "Maternal care for prolapse of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.531", + "display": "Maternal care for retroversion of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.532", + "display": "Maternal care for retroversion of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.533", + "display": "Maternal care for retroversion of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.539", + "display": "Maternal care for retroversion of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.591", + "display": "Maternal care for other abnormalities of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.592", + "display": "Maternal care for other abnormalities of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.593", + "display": "Maternal care for other abnormalities of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.599", + "display": "Maternal care for other abnormalities of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.60", + "display": "Maternal care for abnormality of vagina, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.61", + "display": "Maternal care for abnormality of vagina, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.62", + "display": "Maternal care for abnormality of vagina, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.63", + "display": "Maternal care for abnormality of vagina, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.70", + "display": "Maternal care for abnormality of vulva and perineum, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.71", + "display": "Maternal care for abnormality of vulva and perineum, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.72", + "display": "Maternal care for abnormality of vulva and perineum, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.73", + "display": "Maternal care for abnormality of vulva and perineum, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.80", + "display": "Maternal care for other abnormalities of pelvic organs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.81", + "display": "Maternal care for other abnormalities of pelvic organs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.82", + "display": "Maternal care for other abnormalities of pelvic organs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.83", + "display": "Maternal care for other abnormalities of pelvic organs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.90", + "display": "Maternal care for abnormality of pelvic organ, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.91", + "display": "Maternal care for abnormality of pelvic organ, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.92", + "display": "Maternal care for abnormality of pelvic organ, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.93", + "display": "Maternal care for abnormality of pelvic organ, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX0", + "display": "Maternal care for (suspected) hereditary disease in fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX1", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX2", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX3", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX4", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX5", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX9", + "display": "Maternal care for (suspected) hereditary disease in fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX0", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX1", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX2", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX3", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX4", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX5", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX9", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX0", + "display": "Maternal care for (suspected) damage to fetus from alcohol, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX1", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX2", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX3", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX4", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX5", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX9", + "display": "Maternal care for (suspected) damage to fetus from alcohol, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX0", + "display": "Maternal care for (suspected) damage to fetus by drugs, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX1", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX2", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX3", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX4", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX5", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX9", + "display": "Maternal care for (suspected) damage to fetus by drugs, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX0", + "display": "Maternal care for (suspected) damage to fetus by radiation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX1", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX2", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX3", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX4", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX5", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX9", + "display": "Maternal care for (suspected) damage to fetus by radiation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX0", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX1", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX2", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX3", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX4", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX5", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX9", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX0", + "display": "Maternal care for other (suspected) fetal abnormality and damage, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX1", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX2", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX3", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX4", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX5", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX9", + "display": "Maternal care for other (suspected) fetal abnormality and damage, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX0", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX1", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX2", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX3", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX4", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX5", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX9", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0110", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0111", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0112", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0113", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0114", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0115", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0119", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0120", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0121", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0122", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0123", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0124", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0125", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0129", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0130", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0131", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0132", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0133", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0134", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0135", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0139", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0190", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0191", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0192", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0193", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0194", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0195", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0199", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0910", + "display": "Maternal care for other rhesus isoimmunization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0911", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0912", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0913", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0914", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0915", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0919", + "display": "Maternal care for other rhesus isoimmunization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0920", + "display": "Maternal care for other rhesus isoimmunization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0921", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0922", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0923", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0924", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0925", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0929", + "display": "Maternal care for other rhesus isoimmunization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0930", + "display": "Maternal care for other rhesus isoimmunization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0931", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0932", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0933", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0934", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0935", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0939", + "display": "Maternal care for other rhesus isoimmunization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0990", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0991", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0992", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0993", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0994", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0995", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0999", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1110", + "display": "Maternal care for Anti-A sensitization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1111", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1112", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1113", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1114", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1115", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1119", + "display": "Maternal care for Anti-A sensitization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1120", + "display": "Maternal care for Anti-A sensitization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1121", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1122", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1123", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1124", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1125", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1129", + "display": "Maternal care for Anti-A sensitization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1130", + "display": "Maternal care for Anti-A sensitization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1131", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1132", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1133", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1134", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1135", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1139", + "display": "Maternal care for Anti-A sensitization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1190", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1191", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1192", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1193", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1194", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1195", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1199", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1910", + "display": "Maternal care for other isoimmunization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1911", + "display": "Maternal care for other isoimmunization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1912", + "display": "Maternal care for other isoimmunization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1913", + "display": "Maternal care for other isoimmunization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1914", + "display": "Maternal care for other isoimmunization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1915", + "display": "Maternal care for other isoimmunization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1919", + "display": "Maternal care for other isoimmunization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1920", + "display": "Maternal care for other isoimmunization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1921", + "display": "Maternal care for other isoimmunization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1922", + "display": "Maternal care for other isoimmunization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1923", + "display": "Maternal care for other isoimmunization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1924", + "display": "Maternal care for other isoimmunization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1925", + "display": "Maternal care for other isoimmunization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1929", + "display": "Maternal care for other isoimmunization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1930", + "display": "Maternal care for other isoimmunization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1931", + "display": "Maternal care for other isoimmunization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1932", + "display": "Maternal care for other isoimmunization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1933", + "display": "Maternal care for other isoimmunization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1934", + "display": "Maternal care for other isoimmunization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1935", + "display": "Maternal care for other isoimmunization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1939", + "display": "Maternal care for other isoimmunization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1990", + "display": "Maternal care for other isoimmunization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1991", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1992", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1993", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1994", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1995", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1999", + "display": "Maternal care for other isoimmunization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X0", + "display": "Maternal care for hydrops fetalis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X1", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X2", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X3", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X4", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X5", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X9", + "display": "Maternal care for hydrops fetalis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X0", + "display": "Maternal care for hydrops fetalis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X1", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X2", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X3", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X4", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X5", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X9", + "display": "Maternal care for hydrops fetalis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X0", + "display": "Maternal care for hydrops fetalis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X1", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X2", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X3", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X4", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X5", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X9", + "display": "Maternal care for hydrops fetalis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X0", + "display": "Maternal care for hydrops fetalis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X1", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X2", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X3", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X4", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X5", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X9", + "display": "Maternal care for hydrops fetalis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX0", + "display": "Maternal care for intrauterine death, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX1", + "display": "Maternal care for intrauterine death, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX2", + "display": "Maternal care for intrauterine death, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX3", + "display": "Maternal care for intrauterine death, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX4", + "display": "Maternal care for intrauterine death, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX5", + "display": "Maternal care for intrauterine death, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX9", + "display": "Maternal care for intrauterine death, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5110", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5111", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5112", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5113", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5114", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5115", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5119", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5120", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5121", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5122", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5123", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5124", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5125", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5129", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5130", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5131", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5132", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5133", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5134", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5135", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5139", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5190", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5191", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5192", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5193", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5194", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5195", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5199", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5910", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5911", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5912", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5913", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5914", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5915", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5919", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5920", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5921", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5922", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5923", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5924", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5925", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5929", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5930", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5931", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5932", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5933", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5934", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5935", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5939", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5990", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5991", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5992", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5993", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5994", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5995", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5999", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X0", + "display": "Maternal care for excessive fetal growth, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X1", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X2", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X3", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X4", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X5", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X9", + "display": "Maternal care for excessive fetal growth, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X0", + "display": "Maternal care for excessive fetal growth, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X1", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X2", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X3", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X4", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X5", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X9", + "display": "Maternal care for excessive fetal growth, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X0", + "display": "Maternal care for excessive fetal growth, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X1", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X2", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X3", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X4", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X5", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X9", + "display": "Maternal care for excessive fetal growth, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X0", + "display": "Maternal care for excessive fetal growth, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X1", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X2", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X3", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X4", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X5", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X9", + "display": "Maternal care for excessive fetal growth, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8120", + "display": "Decreased fetal movements, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8121", + "display": "Decreased fetal movements, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8122", + "display": "Decreased fetal movements, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8123", + "display": "Decreased fetal movements, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8124", + "display": "Decreased fetal movements, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8125", + "display": "Decreased fetal movements, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8129", + "display": "Decreased fetal movements, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8130", + "display": "Decreased fetal movements, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8131", + "display": "Decreased fetal movements, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8132", + "display": "Decreased fetal movements, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8133", + "display": "Decreased fetal movements, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8134", + "display": "Decreased fetal movements, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8135", + "display": "Decreased fetal movements, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8139", + "display": "Decreased fetal movements, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8190", + "display": "Decreased fetal movements, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8191", + "display": "Decreased fetal movements, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8192", + "display": "Decreased fetal movements, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8193", + "display": "Decreased fetal movements, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8194", + "display": "Decreased fetal movements, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8195", + "display": "Decreased fetal movements, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8199", + "display": "Decreased fetal movements, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8210", + "display": "Fetal anemia and thrombocytopenia, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8211", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8212", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8213", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8214", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8215", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8219", + "display": "Fetal anemia and thrombocytopenia, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8220", + "display": "Fetal anemia and thrombocytopenia, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8221", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8222", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8223", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8224", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8225", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8229", + "display": "Fetal anemia and thrombocytopenia, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8230", + "display": "Fetal anemia and thrombocytopenia, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8231", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8232", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8233", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8234", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8235", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8239", + "display": "Fetal anemia and thrombocytopenia, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8290", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8291", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8292", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8293", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8294", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8295", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8299", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8310", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8311", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8312", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8313", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8314", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8315", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8319", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8320", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8321", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8322", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8323", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8324", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8325", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8329", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8330", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8331", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8332", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8333", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8334", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8335", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8339", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8390", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8391", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8392", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8393", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8394", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8395", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8399", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8910", + "display": "Maternal care for other specified fetal problems, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8911", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8912", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8913", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8914", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8915", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8919", + "display": "Maternal care for other specified fetal problems, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8920", + "display": "Maternal care for other specified fetal problems, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8921", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8922", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8923", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8924", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8925", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8929", + "display": "Maternal care for other specified fetal problems, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8930", + "display": "Maternal care for other specified fetal problems, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8931", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8932", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8933", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8934", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8935", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8939", + "display": "Maternal care for other specified fetal problems, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8990", + "display": "Maternal care for other specified fetal problems, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8991", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8992", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8993", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8994", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8995", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8999", + "display": "Maternal care for other specified fetal problems, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X0", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X1", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X2", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X3", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X4", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X5", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X9", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X0", + "display": "Maternal care for fetal problem, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X1", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X2", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X3", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X4", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X5", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X9", + "display": "Maternal care for fetal problem, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X0", + "display": "Maternal care for fetal problem, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X1", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X2", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X3", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X4", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X5", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X9", + "display": "Maternal care for fetal problem, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X0", + "display": "Maternal care for fetal problem, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X1", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X2", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X3", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X4", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X5", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X9", + "display": "Maternal care for fetal problem, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX0", + "display": "Polyhydramnios, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX1", + "display": "Polyhydramnios, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX2", + "display": "Polyhydramnios, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX3", + "display": "Polyhydramnios, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX4", + "display": "Polyhydramnios, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX5", + "display": "Polyhydramnios, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX9", + "display": "Polyhydramnios, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX0", + "display": "Polyhydramnios, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX1", + "display": "Polyhydramnios, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX2", + "display": "Polyhydramnios, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX3", + "display": "Polyhydramnios, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX4", + "display": "Polyhydramnios, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX5", + "display": "Polyhydramnios, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX9", + "display": "Polyhydramnios, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX0", + "display": "Polyhydramnios, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX1", + "display": "Polyhydramnios, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX2", + "display": "Polyhydramnios, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX3", + "display": "Polyhydramnios, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX4", + "display": "Polyhydramnios, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX5", + "display": "Polyhydramnios, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX9", + "display": "Polyhydramnios, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX0", + "display": "Polyhydramnios, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX1", + "display": "Polyhydramnios, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX2", + "display": "Polyhydramnios, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX3", + "display": "Polyhydramnios, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX4", + "display": "Polyhydramnios, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX5", + "display": "Polyhydramnios, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX9", + "display": "Polyhydramnios, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X0", + "display": "Oligohydramnios, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X1", + "display": "Oligohydramnios, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X2", + "display": "Oligohydramnios, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X3", + "display": "Oligohydramnios, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X4", + "display": "Oligohydramnios, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X5", + "display": "Oligohydramnios, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X9", + "display": "Oligohydramnios, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X0", + "display": "Oligohydramnios, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X1", + "display": "Oligohydramnios, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X2", + "display": "Oligohydramnios, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X3", + "display": "Oligohydramnios, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X4", + "display": "Oligohydramnios, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X5", + "display": "Oligohydramnios, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X9", + "display": "Oligohydramnios, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X0", + "display": "Oligohydramnios, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X1", + "display": "Oligohydramnios, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X2", + "display": "Oligohydramnios, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X3", + "display": "Oligohydramnios, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X4", + "display": "Oligohydramnios, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X5", + "display": "Oligohydramnios, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X9", + "display": "Oligohydramnios, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X0", + "display": "Oligohydramnios, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X1", + "display": "Oligohydramnios, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X2", + "display": "Oligohydramnios, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X3", + "display": "Oligohydramnios, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X4", + "display": "Oligohydramnios, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X5", + "display": "Oligohydramnios, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X9", + "display": "Oligohydramnios, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1010", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1011", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1012", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1013", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1014", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1015", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1019", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1020", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1021", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1022", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1023", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1024", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1025", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1029", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1030", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1031", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1032", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1033", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1034", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1035", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1039", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1090", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1091", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1092", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1093", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1094", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1095", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1099", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1210", + "display": "Chorioamnionitis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1211", + "display": "Chorioamnionitis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1212", + "display": "Chorioamnionitis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1213", + "display": "Chorioamnionitis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1214", + "display": "Chorioamnionitis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1215", + "display": "Chorioamnionitis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1219", + "display": "Chorioamnionitis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1220", + "display": "Chorioamnionitis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1221", + "display": "Chorioamnionitis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1222", + "display": "Chorioamnionitis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1223", + "display": "Chorioamnionitis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1224", + "display": "Chorioamnionitis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1225", + "display": "Chorioamnionitis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1229", + "display": "Chorioamnionitis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1230", + "display": "Chorioamnionitis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1231", + "display": "Chorioamnionitis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1232", + "display": "Chorioamnionitis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1233", + "display": "Chorioamnionitis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1234", + "display": "Chorioamnionitis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1235", + "display": "Chorioamnionitis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1239", + "display": "Chorioamnionitis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1290", + "display": "Chorioamnionitis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1291", + "display": "Chorioamnionitis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1292", + "display": "Chorioamnionitis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1293", + "display": "Chorioamnionitis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1294", + "display": "Chorioamnionitis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1295", + "display": "Chorioamnionitis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1299", + "display": "Chorioamnionitis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1410", + "display": "Placentitis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1411", + "display": "Placentitis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1412", + "display": "Placentitis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1413", + "display": "Placentitis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1414", + "display": "Placentitis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1415", + "display": "Placentitis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1419", + "display": "Placentitis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1420", + "display": "Placentitis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1421", + "display": "Placentitis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1422", + "display": "Placentitis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1423", + "display": "Placentitis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1424", + "display": "Placentitis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1425", + "display": "Placentitis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1429", + "display": "Placentitis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1430", + "display": "Placentitis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1431", + "display": "Placentitis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1432", + "display": "Placentitis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1433", + "display": "Placentitis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1434", + "display": "Placentitis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1435", + "display": "Placentitis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1439", + "display": "Placentitis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1490", + "display": "Placentitis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1491", + "display": "Placentitis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1492", + "display": "Placentitis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1493", + "display": "Placentitis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1494", + "display": "Placentitis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1495", + "display": "Placentitis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1499", + "display": "Placentitis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X10", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X11", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X12", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X13", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X14", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X15", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X19", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X20", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X21", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X22", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X23", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X24", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X25", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X29", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X30", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X31", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X32", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X33", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X34", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X35", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X39", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X90", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X91", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X92", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X93", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X94", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X95", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X99", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.00", + "display": "Premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.011", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.012", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.013", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.019", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.02", + "display": "Full-term premature rupture of membranes, onset of labor within 24 hours of rupture" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.10", + "display": "Premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.111", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.112", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.113", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.119", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.12", + "display": "Full-term premature rupture of membranes, onset of labor more than 24 hours following rupture" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.90", + "display": "Premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.911", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.912", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.913", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.919", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.92", + "display": "Full-term premature rupture of membranes, unspecified as to length of time between rupture and onset of labor" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.011", + "display": "Fetomaternal placental transfusion syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.012", + "display": "Fetomaternal placental transfusion syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.013", + "display": "Fetomaternal placental transfusion syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.019", + "display": "Fetomaternal placental transfusion syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.021", + "display": "Fetus-to-fetus placental transfusion syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.022", + "display": "Fetus-to-fetus placental transfusion syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.023", + "display": "Fetus-to-fetus placental transfusion syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.029", + "display": "Fetus-to-fetus placental transfusion syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.101", + "display": "Malformation of placenta, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.102", + "display": "Malformation of placenta, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.103", + "display": "Malformation of placenta, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.109", + "display": "Malformation of placenta, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.111", + "display": "Circumvallate placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.112", + "display": "Circumvallate placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.113", + "display": "Circumvallate placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.119", + "display": "Circumvallate placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.121", + "display": "Velamentous insertion of umbilical cord, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.122", + "display": "Velamentous insertion of umbilical cord, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.123", + "display": "Velamentous insertion of umbilical cord, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.129", + "display": "Velamentous insertion of umbilical cord, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.191", + "display": "Other malformation of placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.192", + "display": "Other malformation of placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.193", + "display": "Other malformation of placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.199", + "display": "Other malformation of placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.211", + "display": "Placenta accreta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.212", + "display": "Placenta accreta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.213", + "display": "Placenta accreta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.219", + "display": "Placenta accreta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.221", + "display": "Placenta increta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.222", + "display": "Placenta increta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.223", + "display": "Placenta increta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.229", + "display": "Placenta increta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.231", + "display": "Placenta percreta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.232", + "display": "Placenta percreta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.233", + "display": "Placenta percreta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.239", + "display": "Placenta percreta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.811", + "display": "Placental infarction, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.812", + "display": "Placental infarction, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.813", + "display": "Placental infarction, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.819", + "display": "Placental infarction, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.891", + "display": "Other placental disorders, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.892", + "display": "Other placental disorders, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.893", + "display": "Other placental disorders, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.899", + "display": "Other placental disorders, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.90", + "display": "Unspecified placental disorder, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.91", + "display": "Unspecified placental disorder, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.92", + "display": "Unspecified placental disorder, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.93", + "display": "Unspecified placental disorder, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.00", + "display": "Complete placenta previa NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.01", + "display": "Complete placenta previa NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.02", + "display": "Complete placenta previa NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.03", + "display": "Complete placenta previa NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.10", + "display": "Complete placenta previa with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.11", + "display": "Complete placenta previa with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.12", + "display": "Complete placenta previa with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.13", + "display": "Complete placenta previa with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.20", + "display": "Partial placenta previa NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.21", + "display": "Partial placenta previa NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.22", + "display": "Partial placenta previa NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.23", + "display": "Partial placenta previa NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.30", + "display": "Partial placenta previa with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.31", + "display": "Partial placenta previa with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.32", + "display": "Partial placenta previa with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.33", + "display": "Partial placenta previa with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.40", + "display": "Low lying placenta NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.41", + "display": "Low lying placenta NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.42", + "display": "Low lying placenta NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.43", + "display": "Low lying placenta NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.50", + "display": "Low lying placenta with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.51", + "display": "Low lying placenta with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.52", + "display": "Low lying placenta with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.53", + "display": "Low lying placenta with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.001", + "display": "Premature separation of placenta with coagulation defect, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.002", + "display": "Premature separation of placenta with coagulation defect, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.003", + "display": "Premature separation of placenta with coagulation defect, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.009", + "display": "Premature separation of placenta with coagulation defect, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.011", + "display": "Premature separation of placenta with afibrinogenemia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.012", + "display": "Premature separation of placenta with afibrinogenemia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.013", + "display": "Premature separation of placenta with afibrinogenemia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.019", + "display": "Premature separation of placenta with afibrinogenemia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.021", + "display": "Premature separation of placenta with disseminated intravascular coagulation, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.022", + "display": "Premature separation of placenta with disseminated intravascular coagulation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.023", + "display": "Premature separation of placenta with disseminated intravascular coagulation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.029", + "display": "Premature separation of placenta with disseminated intravascular coagulation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.091", + "display": "Premature separation of placenta with other coagulation defect, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.092", + "display": "Premature separation of placenta with other coagulation defect, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.093", + "display": "Premature separation of placenta with other coagulation defect, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.099", + "display": "Premature separation of placenta with other coagulation defect, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X1", + "display": "Other premature separation of placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X2", + "display": "Other premature separation of placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X3", + "display": "Other premature separation of placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X9", + "display": "Other premature separation of placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.90", + "display": "Premature separation of placenta, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.91", + "display": "Premature separation of placenta, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.92", + "display": "Premature separation of placenta, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.93", + "display": "Premature separation of placenta, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.001", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.002", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.003", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.009", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.011", + "display": "Antepartum hemorrhage with afibrinogenemia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.012", + "display": "Antepartum hemorrhage with afibrinogenemia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.013", + "display": "Antepartum hemorrhage with afibrinogenemia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.019", + "display": "Antepartum hemorrhage with afibrinogenemia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.021", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.022", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.023", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.029", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.091", + "display": "Antepartum hemorrhage with other coagulation defect, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.092", + "display": "Antepartum hemorrhage with other coagulation defect, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.093", + "display": "Antepartum hemorrhage with other coagulation defect, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.099", + "display": "Antepartum hemorrhage with other coagulation defect, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X1", + "display": "Other antepartum hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X2", + "display": "Other antepartum hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X3", + "display": "Other antepartum hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X9", + "display": "Other antepartum hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.90", + "display": "Antepartum hemorrhage, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.91", + "display": "Antepartum hemorrhage, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.92", + "display": "Antepartum hemorrhage, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.93", + "display": "Antepartum hemorrhage, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.00", + "display": "False labor before 37 completed weeks of gestation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.02", + "display": "False labor before 37 completed weeks of gestation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.03", + "display": "False labor before 37 completed weeks of gestation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.1", + "display": "False labor at or after 37 completed weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.9", + "display": "False labor, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O48.0", + "display": "Post-term pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O48.1", + "display": "Prolonged pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.00", + "display": "Preterm labor without delivery, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.02", + "display": "Preterm labor without delivery, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.03", + "display": "Preterm labor without delivery, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.00", + "display": "Rupture of uterus before onset of labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.02", + "display": "Rupture of uterus before onset of labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.03", + "display": "Rupture of uterus before onset of labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.011", + "display": "Air embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.012", + "display": "Air embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.013", + "display": "Air embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.019", + "display": "Air embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.111", + "display": "Amniotic fluid embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.112", + "display": "Amniotic fluid embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.113", + "display": "Amniotic fluid embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.119", + "display": "Amniotic fluid embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.211", + "display": "Thromboembolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.212", + "display": "Thromboembolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.213", + "display": "Thromboembolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.219", + "display": "Thromboembolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.311", + "display": "Pyemic and septic embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.312", + "display": "Pyemic and septic embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.313", + "display": "Pyemic and septic embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.319", + "display": "Pyemic and septic embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.811", + "display": "Other embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.812", + "display": "Other embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.813", + "display": "Other embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.819", + "display": "Other embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O90.3", + "display": "Peripartum cardiomyopathy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.011", + "display": "Infection of nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.012", + "display": "Infection of nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.013", + "display": "Infection of nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.019", + "display": "Infection of nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.111", + "display": "Abscess of breast associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.112", + "display": "Abscess of breast associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.113", + "display": "Abscess of breast associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.119", + "display": "Abscess of breast associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.211", + "display": "Nonpurulent mastitis associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.212", + "display": "Nonpurulent mastitis associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.213", + "display": "Nonpurulent mastitis associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.219", + "display": "Nonpurulent mastitis associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.011", + "display": "Retracted nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.012", + "display": "Retracted nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.013", + "display": "Retracted nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.019", + "display": "Retracted nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.111", + "display": "Cracked nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.112", + "display": "Cracked nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.113", + "display": "Cracked nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.119", + "display": "Cracked nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.20", + "display": "Unspecified disorder of breast associated with pregnancy and the puerperium" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.29", + "display": "Other disorders of breast associated with pregnancy and the puerperium" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.011", + "display": "Tuberculosis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.012", + "display": "Tuberculosis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.013", + "display": "Tuberculosis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.019", + "display": "Tuberculosis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.111", + "display": "Syphilis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.112", + "display": "Syphilis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.113", + "display": "Syphilis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.119", + "display": "Syphilis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.211", + "display": "Gonorrhea complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.212", + "display": "Gonorrhea complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.213", + "display": "Gonorrhea complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.219", + "display": "Gonorrhea complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.311", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.312", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.313", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.319", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.411", + "display": "Viral hepatitis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.412", + "display": "Viral hepatitis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.413", + "display": "Viral hepatitis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.419", + "display": "Viral hepatitis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.511", + "display": "Other viral diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.512", + "display": "Other viral diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.513", + "display": "Other viral diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.519", + "display": "Other viral diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.611", + "display": "Protozoal diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.612", + "display": "Protozoal diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.613", + "display": "Protozoal diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.619", + "display": "Protozoal diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.711", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.712", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.713", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.719", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.811", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.812", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.813", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.819", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.911", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.912", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.913", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.919", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.011", + "display": "Anemia complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.012", + "display": "Anemia complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.013", + "display": "Anemia complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.019", + "display": "Anemia complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.111", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.112", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.113", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.119", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.210", + "display": "Obesity complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.211", + "display": "Obesity complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.212", + "display": "Obesity complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.213", + "display": "Obesity complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.280", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.281", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.282", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.283", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.310", + "display": "Alcohol use complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.311", + "display": "Alcohol use complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.312", + "display": "Alcohol use complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.313", + "display": "Alcohol use complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.320", + "display": "Drug use complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.321", + "display": "Drug use complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.322", + "display": "Drug use complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.323", + "display": "Drug use complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.330", + "display": "Smoking (tobacco) complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.331", + "display": "Smoking (tobacco) complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.332", + "display": "Smoking (tobacco) complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.333", + "display": "Smoking (tobacco) complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.340", + "display": "Other mental disorders complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.341", + "display": "Other mental disorders complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.342", + "display": "Other mental disorders complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.343", + "display": "Other mental disorders complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.350", + "display": "Diseases of the nervous system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.351", + "display": "Diseases of the nervous system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.352", + "display": "Diseases of the nervous system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.353", + "display": "Diseases of the nervous system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.411", + "display": "Diseases of the circulatory system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.412", + "display": "Diseases of the circulatory system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.413", + "display": "Diseases of the circulatory system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.419", + "display": "Diseases of the circulatory system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.511", + "display": "Diseases of the respiratory system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.512", + "display": "Diseases of the respiratory system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.513", + "display": "Diseases of the respiratory system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.519", + "display": "Diseases of the respiratory system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.611", + "display": "Diseases of the digestive system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.612", + "display": "Diseases of the digestive system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.613", + "display": "Diseases of the digestive system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.619", + "display": "Diseases of the digestive system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.711", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.712", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.713", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.719", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.810", + "display": "Abnormal glucose complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.820", + "display": "Streptococcus B carrier state complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.830", + "display": "Other infection carrier state complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.840", + "display": "Bariatric surgery status complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.841", + "display": "Bariatric surgery status complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.842", + "display": "Bariatric surgery status complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.843", + "display": "Bariatric surgery status complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.111", + "display": "Malignant neoplasm complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.112", + "display": "Malignant neoplasm complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.113", + "display": "Malignant neoplasm complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.119", + "display": "Malignant neoplasm complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.211", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.212", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.213", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.219", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.311", + "display": "Physical abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.312", + "display": "Physical abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.313", + "display": "Physical abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.319", + "display": "Physical abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.411", + "display": "Sexual abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.412", + "display": "Sexual abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.413", + "display": "Sexual abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.419", + "display": "Sexual abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.511", + "display": "Psychological abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.512", + "display": "Psychological abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.513", + "display": "Psychological abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.519", + "display": "Psychological abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z33.1", + "display": "Pregnant state, incidental" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z33.3", + "display": "Pregnant state, gestational carrier" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.00", + "display": "Encounter for supervision of normal first pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.01", + "display": "Encounter for supervision of normal first pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.02", + "display": "Encounter for supervision of normal first pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.03", + "display": "Encounter for supervision of normal first pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.80", + "display": "Encounter for supervision of other normal pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.81", + "display": "Encounter for supervision of other normal pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.82", + "display": "Encounter for supervision of other normal pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.83", + "display": "Encounter for supervision of other normal pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.90", + "display": "Encounter for supervision of normal pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.91", + "display": "Encounter for supervision of normal pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.92", + "display": "Encounter for supervision of normal pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.93", + "display": "Encounter for supervision of normal pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.01", + "display": "Less than 8 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.08", + "display": "8 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.09", + "display": "9 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.10", + "display": "10 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.11", + "display": "11 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.12", + "display": "12 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.13", + "display": "13 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.14", + "display": "14 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.15", + "display": "15 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.16", + "display": "16 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.17", + "display": "17 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.18", + "display": "18 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.19", + "display": "19 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.20", + "display": "20 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.21", + "display": "21 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.22", + "display": "22 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.23", + "display": "23 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.24", + "display": "24 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.25", + "display": "25 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.26", + "display": "26 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.27", + "display": "27 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.28", + "display": "28 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.29", + "display": "29 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.30", + "display": "30 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.31", + "display": "31 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.32", + "display": "32 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.33", + "display": "33 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.34", + "display": "34 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.35", + "display": "35 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.36", + "display": "36 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.37", + "display": "37 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.38", + "display": "38 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.39", + "display": "39 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.40", + "display": "40 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.41", + "display": "41 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.42", + "display": "42 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.49", + "display": "Greater than 42 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0000", + "display": "Abdominal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0001", + "display": "Abdominal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00101", + "display": "Right tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00102", + "display": "Left tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00109", + "display": "Unspecified tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00111", + "display": "Right tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00112", + "display": "Left tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00119", + "display": "Unspecified tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00201", + "display": "Right ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00202", + "display": "Left ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00209", + "display": "Unspecified ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00211", + "display": "Right ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00212", + "display": "Left ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00219", + "display": "Unspecified ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0080", + "display": "Other ectopic pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0081", + "display": "Other ectopic pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0090", + "display": "Unspecified ectopic pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0091", + "display": "Unspecified ectopic pregnancy with intrauterine pregnancy" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113883.3.526.3.378" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "DQMFHIRHelpers", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers", + "version": "4.0.1", + "name": "DQMFHIRHelpers", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBEUU1GSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMScKCnVzaW5nIEZISVIgdmVyc2lvbiAnNC4wLjEnCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChwZXJpb2QgRkhJUi5QZXJpb2QpOgogICAgaWYgcGVyaW9kIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvUXVhbnRpdHkocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBxdWFudGl0eS51bml0LnZhbHVlIH0KCmRlZmluZSBmdW5jdGlvbiBUb1JhdGlvKHJhdGlvIEZISVIuUmF0aW8pOgogICAgaWYgcmF0aW8gaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5SYXRpbyB7IG51bWVyYXRvcjogVG9RdWFudGl0eShyYXRpby5udW1lcmF0b3IpLCBkZW5vbWluYXRvcjogVG9RdWFudGl0eShyYXRpby5kZW5vbWluYXRvcikgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocmFuZ2UgRkhJUi5SYW5nZSk6CiAgICBpZiByYW5nZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgSW50ZXJ2YWxbVG9RdWFudGl0eShyYW5nZS5sb3cpLCBUb1F1YW50aXR5KHJhbmdlLmhpZ2gpXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29kZShjb2RpbmcgRkhJUi5Db2RpbmcpOgogICAgaWYgY29kaW5nIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uQ29kZSB7CiAgICAgICAgICBjb2RlOiBjb2RpbmcuY29kZS52YWx1ZSwKICAgICAgICAgIHN5c3RlbTogY29kaW5nLnN5c3RlbS52YWx1ZSwKICAgICAgICAgIHZlcnNpb246IGNvZGluZy52ZXJzaW9uLnZhbHVlLAogICAgICAgICAgZGlzcGxheTogY29kaW5nLmRpc3BsYXkudmFsdWUKICAgICAgICB9CgpkZWZpbmUgZnVuY3Rpb24gVG9Db25jZXB0KGNvbmNlcHQgRkhJUi5Db2RlYWJsZUNvbmNlcHQpOgogICAgaWYgY29uY2VwdCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvbmNlcHQgewogICAgICAgICAgICBjb2RlczogY29uY2VwdC5jb2RpbmcgQyByZXR1cm4gVG9Db2RlKEMpLAogICAgICAgICAgICBkaXNwbGF5OiBjb25jZXB0LnRleHQudmFsdWUKICAgICAgICB9CgoKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjY291bnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ2FyZGluYWxpdHlCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Db25kaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkdyb3VwaW5nQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblByZWNoZWNrQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVsYXRpb25zaGlwVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25SZXF1aXJlZEJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblNlbGVjdGlvbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5RGVmaW5pdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aXZpdHlQYXJ0aWNpcGFudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1VzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZG1pbmlzdHJhdGl2ZUdlbmRlcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZHZlcnNlRXZlbnRBY3R1YWxpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWdncmVnYXRpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXBwb2ludG1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBc3NlcnRpb25PcGVyYXRvclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRBZ2VudE5ldHdvcmtUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpbmRpbmdTdHJlbmd0aCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdENhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJ1bmRsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FwYWJpbGl0eVN0YXRlbWVudEtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eUtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhbkludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlVGVhbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsYWltU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsaW5pY2FsSW1wcmVzc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU2VhcmNoU3VwcG9ydCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU3lzdGVtQ29udGVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUhpZXJhcmNoeU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tbXVuaWNhdGlvblByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcGFydG1lbnRDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvbkF0dGVzdGF0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25jZXB0TWFwRXF1aXZhbGVuY2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEdyb3VwVW5tYXBwZWRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsRGVsZXRlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsUmVhZFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zZW50RGF0YU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFByb3Zpc2lvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnN0cmFpbnRTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250YWN0UG9pbnRTeXN0ZW0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0UHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJhY3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJpYnV0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvdmVyYWdlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEN1cnJlbmN5Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEYXlPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5c09mV2Vlayk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXRlY3RlZElzc3VlU2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDb2xvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VOYW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZVVzZVN0YXRlbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaWFnbm9zdGljUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERpc2NyaW1pbmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50Q29uZmlkZW50aWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlZmVyZW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbGlnaWJpbGl0eVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmFibGVXaGVuQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuZHBvaW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFcGlzb2RlT2ZDYXJlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50Q2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZlbnRUaW1pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZpZGVuY2VWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cG9zdXJlU3RhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXh0ZW5zaW9uQ29udGV4dFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkFsbFR5cGVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJEZWZpbmVkVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGV2aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlN1YnN0YW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSVmVyc2lvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGYW1pbHlIaXN0b3J5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZpbHRlck9wZXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZsYWdTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR29hbExpZmVjeWNsZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50UnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdyb3VwTWVhc3VyZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGFuY2VSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhZ2VHZW5lcmF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRlUGFyYW1ldGVyQ29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBIVFRQVmVyYik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGlmaWVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElkZW50aXR5QXNzdXJhbmNlTGV2ZWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1hZ2luZ1N0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1tdW5pemF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VQcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW52b2ljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElzc3VlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIExvY2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lYXN1cmVSZXBvcnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGlhU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uRGlzcGVuc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1pbWVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWVVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYXJyYXRpdmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTm90ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTnV0cml0aWlvbk9yZGVySW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlvbk9yZGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uRGF0YVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT2JzZXJ2YXRpb25SYW5nZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT3BlcmF0aW9uUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9yaWVudGF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJhbWV0ZXJVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRSZXF1aXJlZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGFudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnROb3RpY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb2NlZHVyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVJlcHJlc2VudGF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb3BlcnR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm92ZW5hbmNlRW50aXR5Um9sZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQdWJsaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFsaXR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFudGl0eUNvbXBhcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1PcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWVzdGlvbm5haXJlSXRlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZUhhbmRsaW5nUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZVZlcnNpb25SdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcnJlZERvY3VtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbGF0ZWRBcnRpZmFjdFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVtaXR0YW5jZU91dGNvbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVwb3NpdG9yeVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaEVsZW1lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3R1ZHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hTdWJqZWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc291cmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVZlcnNpb25Qb2xpY3kpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzcG9uc2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3RmdWxDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSaXNrQXNzZXNzbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTUERYTGljZW5zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaEVudHJ5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hNb2RpZmllckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoUGFyYW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlY3Rpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcXVlbmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcnZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNsaWNpbmdSdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbG90U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNvcnREaXJlY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3BlY2ltZW5Db250YWluZWRQcmVmZXJlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJhbmRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcENvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcEdyb3VwVHlwZU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwSW5wdXRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcE1vZGVsTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBTb3VyY2VMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uQ2hhbm5lbFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseURlbGl2ZXJ5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGFza1ByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydEFjdGlvblJlc3VsdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRSZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0U2NyaXB0UmVxdWVzdE1ldGhvZENvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHJpZ2dlclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHlwZURlcml2YXRpb25SdWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVSZXN0ZnVsSW50ZXJhY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVURJRW50cnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVuaXRzT2ZUaW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uQmFzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25FeWVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBYUGF0aFVzYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBiYXNlNjRCaW5hcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgaWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9Cb29sZWFuKHZhbHVlIGJvb2xlYW4pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlKHZhbHVlIGRhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBkYXRlVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RlY2ltYWwodmFsdWUgZGVjaW1hbCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RhdGVUaW1lKHZhbHVlIGluc3RhbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlZ2VyKHZhbHVlIGludGVnZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgc3RyaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvVGltZSh2YWx1ZSB0aW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHVyaSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB4aHRtbCk6IHZhbHVlLnZhbHVlCg==" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iRFFNRkhJUkhlbHBlcnMiIHN5c3RlbT0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iIHZlcnNpb249IjQuMC4xIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzoxLTM6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTk6NTgiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY6NS05OjU4IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjY6OC02OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjo4LTY6MTMiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc6OS03OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTo5LTk6NTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5OjE4LTk6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk6MTgtOTozMSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOToxOC05OjIzIiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTo0MC05OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NTEiIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NDUiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InBlcmlvZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OjM1LTU6NDUiIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTE6MS0xNTo4MiIgbmFtZT0iVG9RdWFudGl0eSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI6NS0xNTo4MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMjo4LTEyOjIzIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTI6OC0xMjoxNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzOjktMTM6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTU6OS0xNTo4MiIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTozNC0xNTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6MzQtMTU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1OjM0LTE1OjQxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTo2Mi0xNTo4MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NzQiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NjkiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTE6MzctMTE6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTIxOjEwNyIgbmFtZT0iVG9SYXRpbyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6NS0yMToxMDciIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTg6OC0xODoyMCIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjgtMTg6MTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OlJhdGlvIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxOTo5LTE5OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjIxOjktMjE6MTA3IiBjbGFzc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9Im51bWVyYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTozNS0yMTo2MSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE6NDYtMjE6NjAiIHBhdGg9Im51bWVyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTo0Ni0yMTo1MCIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkZW5vbWluYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTo3Ny0yMToxMDUiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjg4LTIxOjEwNCIgcGF0aD0iZGVub21pbmF0b3IiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6ODgtMjE6OTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhdGlvIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3OjMxLTE3OjQwIiBuYW1lPSJmaGlyOlJhdGlvIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM6MS0yNzo2MyIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ6NS0yNzo2MyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIyNDo4LTI0OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ6OC0yNDoxMiIgbmFtZT0icmFuZ2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTo5LTI1OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjc6OS0yNzo2MyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjI3OjE4LTI3OjM4IiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoyOS0yNzozNyIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjI5LTI3OjMzIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjc6NDEtMjc6NjIiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjUyLTI3OjYxIiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjUyLTI3OjU2IiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyYW5nZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzozNC0yMzo0MyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzg6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDo1LTM4OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMzA6OC0zMDoyMSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwOjgtMzA6MTMiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTo5LTMxOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjMzOjktMzg6OSIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMzQ6MTctMzQ6MzMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjIyIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIzNToxOS0zNTozNyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU6MTktMzU6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNToxOS0zNToyNCIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2ZXJzaW9uIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM2OjIwLTM2OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkaXNwbGF5Ij4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM3OjIwLTM3OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29kaW5nIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5OjMxLTI5OjQxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDc6OSIgbmFtZT0iVG9Db25jZXB0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MTo1LTQ3OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDE6OC00MToyMiIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjgtNDE6MTQiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29uY2VwdCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6OS00MjoxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI0NDo5LTQ3OjkiIGNsYXNzVHlwZT0idDpDb25jZXB0IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJjb2RlcyI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NToyMC00NTo1MiIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToyMC00NTozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NToyMC00NTozMyIgcGF0aD0iY29kaW5nIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iNDU6MzctNDU6NTIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NTo0NC00NTo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo1MSIgbmFtZT0iQyIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iZGlzcGxheSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NjoyMi00NjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MzMiIHBhdGg9InRleHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MjgiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwOjM1LTQwOjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUwOjEtNTA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1MDo0OC01MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTA6NDgtNTA6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUwOjMyLTUwOjQ0IiBuYW1lPSJmaGlyOkFjY291bnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTE6NjAtNTE6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjYwLTUxOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1MTozMi01MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjUyOjU0LTUyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo1NC01Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTI6MzItNTI6NTAiIG5hbWU9ImZoaXI6QWN0aW9uQ29uZGl0aW9uS2luZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUzOjEtNTM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Mzo1Ny01Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6NTctNTM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUzOjMyLTUzOjUzIiBuYW1lPSJmaGlyOkFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NDoxLTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTQ6NTYtNTQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjU2LTU0OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NDozMi01NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NToxLTU1OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTU6NTctNTU6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU1OjU3LTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NTozMi01NTo1MyIgbmFtZT0iZmhpcjpBY3Rpb25QcmVjaGVja0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTY6MS01Njo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjU2OjU3LTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Njo1Ny01Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTY6MzItNTY6NTMiIG5hbWU9ImZoaXI6QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Nzo1Ny01Nzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTc6NTctNTc6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU3OjMyLTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1ODoxLTU4OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTg6NTgtNTg6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU4OjU4LTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1ODozMi01ODo1NCIgbmFtZT0iZmhpcjpBY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtNTk6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1OTo1Ny01OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTk6NTctNTk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU5OjMyLTU5OjUzIiBuYW1lPSJmaGlyOkFjdGl2aXR5RGVmaW5pdGlvbktpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MDoxLTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjA6NTgtNjA6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYwOjU4LTYwOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2MDozMi02MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYxOjEtNjE6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2MTo0Ni02MTo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjE6NDYtNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYxOjMyLTYxOjQyIiBuYW1lPSJmaGlyOkFkZHJlc3NUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjI6MS02Mjo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYyOjQ1LTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Mjo0NS02Mjo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjI6MzItNjI6NDEiIG5hbWU9ImZoaXI6QWRkcmVzc1VzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYzOjEtNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2Mzo1NS02Mzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjM6NTUtNjM6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYzOjMyLTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY0OjU2LTY0OjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NDo1Ni02NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjQ6MzItNjQ6NTIiIG5hbWU9ImZoaXI6QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjU6MS02NTo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY1OjUwLTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NTo1MC02NTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjU6MzItNjU6NDYiIG5hbWU9ImZoaXI6QWdncmVnYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjY6MS02Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY2OjYxLTY2OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Njo2MS02Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjY6MzItNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NzoxLTY3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjc6NjQtNjc6NzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjY3OjY0LTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2NzozMi02Nzo2MCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2ODo2MS02ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjg6NjEtNjg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjY4OjMyLTY4OjU3IiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjk6MS02OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY5OjU3LTY5OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2OTo1Ny02OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjk6MzItNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcwOjEtNzA6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3MDo1Mi03MDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzA6NTItNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcwOjMyLTcwOjQ4IiBuYW1lPSJmaGlyOkFwcG9pbnRtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzE6MS03MTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjcxOjU3LTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3MTo1Ny03MTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzE6MzItNzE6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcyOjEtNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mjo1Ni03Mjo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzI6NTYtNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcyOjMyLTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjczOjEtNzM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mzo1Ny03Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzM6NTctNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjczOjMyLTczOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvblJlc3BvbnNlVHlwZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NDoxLTc0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzQ6NTEtNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc0OjUxLTc0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NDozMi03NDo0NyIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50QWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzU6MS03NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijc1OjYxLTc1OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3NTo2MS03NTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzU6MzItNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NjoxLTc2OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzY6NTItNzY6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc2OjUyLTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NjozMi03Njo0OCIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50T3V0Y29tZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc3OjEtNzc6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Nzo1MC03Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzc6NTAtNzc6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc3OjMyLTc3OjQ2IiBuYW1lPSJmaGlyOkJpbmRpbmdTdHJlbmd0aCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc4OjEtNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3ODo2OS03ODo3OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzg6NjktNzg6NzMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc4OjMyLTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTc5Ojc3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzk6NjctNzk6NzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc5OjY3LTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3OTozMi03OTo2MyIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgwOjEtODA6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDo3My04MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODA6NzMtODA6NzciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgwOjMyLTgwOjY5IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODE6MS04MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgxOjQ1LTgxOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4MTo0NS04MTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODE6MzItODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgyOjEtODI6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Mjo1OC04Mjo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODI6NTgtODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjMyLTgyOjU0IiBuYW1lPSJmaGlyOkNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODM6MS04Mzo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgzOjU1LTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4Mzo1NS04Mzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODM6MzItODM6NTEiIG5hbWU9ImZoaXI6Q2FyZVBsYW5BY3Rpdml0eUtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NDoxLTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODQ6NTctODQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg0OjU3LTg0OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NDozMi04NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODU6MS04NTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg1OjQ5LTg1OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4NTo0OS04NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODU6MzItODU6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5JbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NjoxLTg2OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODY6NDktODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg2OjQ5LTg2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NjozMi04Njo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg3OjEtODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo0OS04Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODc6NDktODc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg3OjMyLTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODg6MS04ODo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg4OjU5LTg4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1OS04ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6MzItODg6NTUiIG5hbWU9ImZoaXI6Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODk6MS04OTo4MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg5OjczLTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OTo3My04OTo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6MzItODk6NjkiIG5hbWU9ImZoaXI6Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MDoxLTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTA6NTEtOTA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjUxLTkwOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MDozMi05MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTE6MS05MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkxOjU0LTkxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1NC05MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6MzItOTE6NTAiIG5hbWU9ImZoaXI6Q2xhaW1SZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjkyOjEtOTI6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mjo0Ni05Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NDYtOTI6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjMyLTkyOjQyIiBuYW1lPSJmaGlyOkNsYWltU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTM6MS05Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkzOjU5LTkzOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mzo1OS05Mzo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTM6MzItOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTQ6MS05NDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk0OjUyLTk0OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5NDo1Mi05NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTQ6MzItOTQ6NDgiIG5hbWU9ImZoaXI6Q29kZVNlYXJjaFN1cHBvcnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NToxLTk1OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTU6NTYtOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk1OjU2LTk1OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NTozMi05NTo1MiIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtQ29udGVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NjoxLTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTY6NjEtOTY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjYxLTk2OjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjozMi05Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk3OjEtOTc6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Nzo1Ni05Nzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTc6NTYtOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk3OjMyLTk3OjUyIiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25Qcmlvcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk4OjEtOTg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5ODo2MS05ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTg6NjEtOTg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk4OjMyLTk4OjU3IiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTk6MS05OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk5OjU0LTk5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OTo1NC05OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTk6MzItOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwMDoxLTEwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwMDo1MC0xMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMDo1MC0xMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwMDozMi0xMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAxOjEtMTAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAxOjUwLTEwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAxOjUwLTEwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAxOjMyLTEwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDI6MS0xMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDI6NjEtMTAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDI6NjEtMTAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDI6MzItMTAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAzOjEtMTAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAzOjUyLTEwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAzOjUyLTEwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAzOjMyLTEwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNDoxLTEwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNDozMi0xMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA1OjEtMTA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA1OjYyLTEwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjYyLTEwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA1OjMyLTEwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDY6MS0xMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDY6NTgtMTA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6NTgtMTA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDY6MzItMTA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA3OjEtMTA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA3OjU2LTEwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA3OjU2LTEwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA3OjMyLTEwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDg6MS0xMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDg6NTMtMTA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDg6NTMtMTA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDg6MzItMTA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOToxLTEwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwOTo1NS0xMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwOTo1NS0xMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwOTozMi0xMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTA6MS0xMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTA6NDctMTEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTA6NDctMTEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTA6MzItMTEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExMToxLTExMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExMTo1My0xMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExMTo1My0xMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExMTozMi0xMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTEyOjEtMTEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEyOjUzLTExMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTEyOjUzLTExMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTEyOjMyLTExMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTM6MS0xMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTM6NTAtMTEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTM6NTAtMTEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTM6MzItMTEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNDoxLTExNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNDo2MC0xMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNDo2MC0xMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNDozMi0xMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNToxLTExNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTo0OS0xMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNTo0OS0xMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNTozMi0xMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTY6MS0xMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTY6NTAtMTE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTY6NTAtMTE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTY6MzItMTE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNzoxLTExNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNzo0OS0xMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0OS0xMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNzozMi0xMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTg6MS0xMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDctMTE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTg6NDctMTE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzItMTE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOToxLTExOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExOTo0NC0xMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExOTo0NC0xMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExOTozMi0xMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIwOjEtMTIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIwOjQ1LTEyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIwOjQ1LTEyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIwOjMyLTEyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIxOjEtMTIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIxOjU2LTEyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIxOjU2LTEyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIxOjMyLTEyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjI6MS0xMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjI6NTQtMTIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjI6NTQtMTIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjI6MzItMTIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjM6MS0xMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjM6NjMtMTIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjM6NjMtMTIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjM6MzItMTIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjQ6MS0xMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjQ6NjItMTI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjQ6NjItMTI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzItMTI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyNTo1NS0xMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyNTo1NS0xMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyNTozMi0xMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjY6MS0xMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjY6NTItMTI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjY6NTItMTI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjY6MzItMTI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI3OjEtMTI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI3OjY0LTEyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTI3OjY0LTEyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjMyLTEyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyODoxLTEyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyODo0OS0xMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyODo0OS0xMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozMi0xMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjk6NTQtMTI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjk6NTQtMTI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzItMTI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzA6NTktMTMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzA6NTktMTMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzItMTMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMToxLTEzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMTozMi0xMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMjoxLTEzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMjozMi0xMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzM6MS0xMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzM6NTgtMTMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzM6NTgtMTMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzM6MzItMTMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM0OjQ3LTEzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM0OjQ3LTEzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjMyLTEzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzU6MS0xMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzU6NTgtMTM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzU6NTgtMTM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6MzItMTM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM2OjEtMTM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM2OjU5LTEzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM2OjU5LTEzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM2OjMyLTEzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzc6MS0xMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzc6NjAtMTM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzc6NjAtMTM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6MzItMTM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzg6MS0xMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzg6NTktMTM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6NTktMTM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzg6MzItMTM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzOTo2MS0xMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzOTo2MS0xMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTozMi0xMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDA6MS0xNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDA6NjAtMTQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6NjAtMTQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzItMTQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDE6MS0xNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDE6NTMtMTQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6NTMtMTQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDE6MzItMTQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MjoxLTE0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MjozMi0xNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDM6MS0xNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDM6NTAtMTQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDM6NTAtMTQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6MzItMTQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NDozMi0xNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDU6MS0xNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDU6NTgtMTQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6NTgtMTQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzItMTQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ2OjEtMTQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ2OjU5LTE0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ2OjU5LTE0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ2OjMyLTE0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDc6MS0xNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDc6NTQtMTQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDc6NTQtMTQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6MzItMTQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDg6MS0xNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDg6NTQtMTQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6NTQtMTQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDg6MzItMTQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDk6MS0xNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDk6NDYtMTQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6NDYtMTQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzItMTQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjU1LTE1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjU1LTE1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MToxLTE1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTozMi0xNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjYxLTE1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjYxLTE1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MzoxLTE1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MzozMi0xNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NDoxLTE1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NDozMi0xNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NDctMTU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NDctMTU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NjoxLTE1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Njo1MC0xNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Njo1MC0xNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NjozMi0xNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU3OjEtMTU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU3OjUxLTE1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU3OjUxLTE1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU3OjMyLTE1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU4OjEtMTU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU4OjUxLTE1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU4OjUxLTE1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU4OjMyLTE1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU0LTE1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU0LTE1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjQ2LTE2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjQ2LTE2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo1NC0xNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo1NC0xNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MjoxLTE2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MjozMi0xNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NDUtMTYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NDUtMTYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjQ6MS0xNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjQ6NTQtMTY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjQ6NTQtMTY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjQ6MzItMTY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTUtMTY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTUtMTY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY2OjEtMTY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY2OjU0LTE2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY2OjU0LTE2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY2OjMyLTE2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY3OjEtMTY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY3OjMyLTE2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjg6MS0xNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjg6NDQtMTY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjg6NDQtMTY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjg6MzItMTY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1NC0xNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1NC0xNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MToxLTE3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MTo1My0xNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MTo1My0xNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MTozMi0xNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTcyOjEtMTcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTcyOjQzLTE3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTcyOjQzLTE3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTcyOjMyLTE3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo1My0xNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo1My0xNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc2OjEtMTc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc2OjYzLTE3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc2OjYzLTE3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc2OjMyLTE3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUzLTE3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUzLTE3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjAtMTc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjAtMTc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NDgtMTc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NDgtMTc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODA6MS0xODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODA6NDgtMTgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODA6NDgtMTgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODA6MzItMTgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODE6MS0xODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6NDQtMTgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODE6NDQtMTgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODE6MzItMTgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo0My0xODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo0My0xODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NDYtMTgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NDYtMTgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjQzLTE4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjQzLTE4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NToxLTE4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4NTo0NS0xODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4NTo0NS0xODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NTozMi0xODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg3OjEtMTg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg3OjMyLTE4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4ODoxLTE4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4ODo1NC0xODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4ODo1NC0xODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4ODozMi0xODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OToxLTE4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTo1Mi0xODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OTo1Mi0xODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4OTozMi0xODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTA6MS0xOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTA6NDYtMTkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTA6NDYtMTkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTA6MzItMTkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjY1LTE5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjY1LTE5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTI6MS0xOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTI6NTktMTkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTI6NTktMTkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTI6MzItMTkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NDoxLTE5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NDozMi0xOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTU6MS0xOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTU6NjAtMTk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTU6NjAtMTk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTU6MzItMTk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTY6MS0xOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTY6NTgtMTk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTY6NTgtMTk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTY6MzItMTk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk3OjEtMTk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjYwLTE5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk3OjYwLTE5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk3OjMyLTE5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjUxLTE5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjUxLTE5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk5OjEtMTk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk5OjYyLTE5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk5OjYyLTE5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk5OjMyLTE5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDA6MS0yMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDA6NjUtMjAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDA6NjUtMjAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDA6MzItMjAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMToxLTIwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMTo0My0yMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMTo0My0yMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMTozMi0yMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NDItMjAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NDItMjAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDM6MS0yMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDM6NjEtMjAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDM6NjEtMjAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDM6MzItMjAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA0OjEtMjA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA0OjUxLTIwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA0OjUxLTIwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA0OjMyLTIwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjUwLTIwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjUwLTIwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NDMtMjA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NDMtMjA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTUtMjA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTUtMjA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA5OjEtMjA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA5OjU0LTIwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA5OjU0LTIwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA5OjMyLTIwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEwOjEtMjEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEwOjU5LTIxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEwOjU5LTIxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEwOjMyLTIxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTE6MS0yMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTE6NTItMjExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTE6NTItMjExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTE6MzItMjExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjQ4LTIxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjQ4LTIxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEzOjEtMjEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEzOjU2LTIxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEzOjU2LTIxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEzOjMyLTIxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTQ6MS0yMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTQ6NTAtMjE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTQ6NTAtMjE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTQ6MzItMjE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE2OjEtMjE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjU0LTIxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE2OjU0LTIxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE2OjMyLTIxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE3OjEtMjE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE3OjUyLTIxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE3OjUyLTIxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE3OjMyLTIxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxODoxLTIxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODo1NC0yMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxODo1NC0yMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxODozMi0yMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo1NC0yMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo1NC0yMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyMDoxLTIyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyMDozMi0yMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjUwLTIyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjUwLTIyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTctMjIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTctMjIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NDctMjIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NDctMjIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNDoxLTIyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNDozMi0yMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjU6NTItMjI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjU6NTItMjI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6MzItMjI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI2OjEtMjI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI2OjQ2LTIyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI2OjQ2LTIyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI2OjMyLTIyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNzoxLTIyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNzo1My0yMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNzo1My0yMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNzozMi0yMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI4OjEtMjI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI4OjYwLTIyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI4OjYwLTIyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI4OjMyLTIyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI5OjEtMjI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI5OjU2LTIyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI5OjU2LTIyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI5OjMyLTIyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NjItMjMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NjItMjMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzI6MS0yMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzI6NTYtMjMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzI6NTYtMjMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzI6MzItMjMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzozMi0yMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNDoxLTIzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNDozMi0yMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNToxLTIzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNTozMi0yMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzY6MS0yMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6NDktMjM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzY6NDktMjM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzY6MzItMjM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM3OjEtMjM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM3OjQ4LTIzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM3OjQ4LTIzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM3OjMyLTIzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM4OjEtMjM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM4OjUwLTIzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM4OjUwLTIzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM4OjMyLTIzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzk6MS0yMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzk6NDgtMjM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzk6NDgtMjM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzk6MzItMjM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NTQtMjQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NTQtMjQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTQtMjQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTQtMjQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDI6MS0yNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDI6NTYtMjQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDI6NTYtMjQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDI6MzItMjQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MzoxLTI0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MzozMi0yNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ0OjEtMjQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ0OjU2LTI0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ0OjU2LTI0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ0OjMyLTI0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NDctMjQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NDctMjQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NjoxLTI0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NjozMi0yNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ3OjEtMjQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ3OjU1LTI0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ3OjU1LTI0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ3OjMyLTI0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0ODoxLTI0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0ODozMi0yNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NTEtMjQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NTEtMjQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTA6MS0yNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTA6NTAtMjUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTA6NTAtMjUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTA6MzItMjUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1My0yNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1My0yNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjUwLTI1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjUwLTI1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTM6MS0yNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTM6NDYtMjUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTM6NDYtMjUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTM6MzItMjUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU0OjEtMjU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU0OjQ3LTI1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU0OjQ3LTI1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU0OjMyLTI1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NTUtMjU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NTUtMjU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU2OjEtMjU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU2OjU3LTI1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU2OjU3LTI1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU2OjMyLTI1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjU1LTI1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjU1LTI1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1ODoxLTI1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1ODozMi0yNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjQ1LTI1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjQ1LTI1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ4LTI2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ4LTI2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYxOjEtMjYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYxOjYyLTI2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYxOjYyLTI2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjMyLTI2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjI6MS0yNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjI6NDktMjYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjI6NDktMjYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjI6MzItMjYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYzOjEtMjYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYzOjQxLTI2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYzOjQxLTI2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYzOjMyLTI2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NDUtMjY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NDUtMjY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTgtMjY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTgtMjY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU4LTI2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU4LTI2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2NzoxLTI2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2NzozMi0yNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2ODoxLTI2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2ODozMi0yNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY5OjU2LTI2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY5OjU2LTI2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY5OjMyLTI2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzA6MS0yNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzA6NjEtMjcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6NjEtMjcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzA6MzItMjcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcxOjEtMjcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcxOjYxLTI3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcxOjYxLTI3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcxOjMyLTI3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MjoxLTI3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MjozMi0yNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjczOjEtMjczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjczOjU4LTI3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjczOjU4LTI3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjczOjMyLTI3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1My0yNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1My0yNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc1OjEtMjc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc1OjU1LTI3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc1OjU1LTI3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc1OjMyLTI3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NjoxLTI3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Njo1NC0yNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Njo1NC0yNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NjozMi0yNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NzoxLTI3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NzozMi0yNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc4OjEtMjc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc4OjQ1LTI3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc4OjQ1LTI3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc4OjMyLTI3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc5OjEtMjc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc5OjQ3LTI3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc5OjQ3LTI3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc5OjMyLTI3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDUtMjgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDUtMjgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NTctMjgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NTctMjgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODI6MS0yODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODI6NjAtMjgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODI6NjAtMjgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODI6MzItMjgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NTEtMjgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NTEtMjgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODQ6MS0yODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODQ6NTEtMjg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODQ6NTEtMjg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODQ6MzItMjg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODU6MS0yODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODU6NjItMjg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODU6NjItMjg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODU6MzItMjg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODc6MS0yODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODc6NTMtMjg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODc6NTMtMjg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6MzItMjg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkwOjEtMjkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkwOjQ2LTI5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkwOjQ2LTI5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkwOjMyLTI5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MToxLTI5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MTozOC0yOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5MTozOC0yOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MTozMi0yOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkyOjEtMjkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkyOjMyLTI5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTM6MS0yOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTM6NDUtMjkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTM6NDUtMjkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTM6MzItMjkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTQ6MS0yOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTQ6NDUtMjk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTQ6NDUtMjk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTQ6MzItMjk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NDctMjk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NDctMjk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NjoxLTI5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NjozMi0yOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTc6MS0yOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTc6NDctMjk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTc6NDctMjk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTc6MzItMjk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5ODoxLTI5ODo0NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5ODozNy0yOTg6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5ODozNy0yOTg6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5ODozMi0yOTg6MzMiIG5hbWU9ImZoaXI6aWQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTk6MS0yOTk6NTMiIG5hbWU9IlRvQm9vbGVhbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjQzLTI5OTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjQzLTI5OTo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMzLTI5OTozOSIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzAwOjEtMzAwOjQ3IiBuYW1lPSJUb0RhdGUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMDozNy0zMDA6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMDozNy0zMDA6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMDozMC0zMDA6MzMiIG5hbWU9ImZoaXI6ZGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1NSIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzAxOjQ1LTMwMTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzAxOjQ1LTMwMTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzAxOjM0LTMwMTo0MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMjoxLTMwMjo1MyIgbmFtZT0iVG9EZWNpbWFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDMtMzAyOjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDMtMzAyOjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzMtMzAyOjM5IiBuYW1lPSJmaGlyOmRlY2ltYWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NTQiIG5hbWU9IlRvRGF0ZVRpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMzo0NC0zMDM6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMzo0NC0zMDM6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMzozNC0zMDM6NDAiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNDoxLTMwNDo1MyIgbmFtZT0iVG9JbnRlZ2VyIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDQ6NDMtMzA0OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDQ6NDMtMzA0OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDQ6MzMtMzA0OjM5IiBuYW1lPSJmaGlyOmludGVnZXIiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDU6MS0zMDU6NTEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDU6NDEtMzA1OjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDU6NDEtMzA1OjQ1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6MzItMzA1OjM3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNjoxLTMwNjo0NyIgbmFtZT0iVG9UaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6MzctMzA2OjQ3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6MzctMzA2OjQxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzAtMzA2OjMzIiBuYW1lPSJmaGlyOnRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDc6MS0zMDc6NDgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDc6MzgtMzA3OjQ4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDc6MzgtMzA3OjQyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDc6MzItMzA3OjM0IiBuYW1lPSJmaGlyOnVyaSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwODoxLTMwODo1MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODo0MC0zMDg6NTAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwODo0MC0zMDg6NDQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwODozMi0zMDg6MzYiIG5hbWU9ImZoaXI6eGh0bWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgPC9zdGF0ZW1lbnRzPgo8L2xpYnJhcnk+Cg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/DQMFHIRHelpers" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113762.1.4.1", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.113762.1.4.1" + } + ], + "version": "20150331", + "name": "ONCAdministrativeSex", + "title": "ONC Administrative Sex", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for ONC Administrative Sex.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", + "version": "HL7V3.0_2020-11", + "code": "F", + "display": "Female" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", + "version": "HL7V3.0_2020-11", + "code": "M", + "display": "Male" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113762.1.4.1" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "FHIRCommon", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon", + "version": "4.0.1", + "name": "FHIRCommon", + "title": "Library - FHIR Common", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "A Shared library encapsulating valuable common terminologies and functions used in FHIR-based CQL artifacts.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT", + "resource": "http://snomed.info/sct" + }, + { + "type": "depends-on", + "display": "Code system RoleCode", + "resource": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" + }, + { + "type": "depends-on", + "display": "Code system Diagnosis Role", + "resource": "http://terminology.hl7.org/CodeSystem/diagnosis-role" + }, + { + "type": "depends-on", + "display": "Code system RequestIntent", + "resource": "http://terminology.hl7.org/CodeSystem/request-intent" + }, + { + "type": "depends-on", + "display": "Code system MedicationRequestCategory", + "resource": "http://terminology.hl7.org/CodeSystem/medicationrequest-category" + }, + { + "type": "depends-on", + "display": "Code system ConditionClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Code system AllergyIntoleranceClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical" + }, + { + "type": "depends-on", + "display": "Code system AllergyIntoleranceVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification" + } + ], + "parameter": [ + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBDb21tb24gdGVybWlub2xvZ2llcyBhbmQgZnVuY3Rpb25zIHVzZWQgaW4gRkhJUi1iYXNlZCBDUUwgYXJ0aWZhY3RzCiovCgpsaWJyYXJ5IEZISVJDb21tb24gdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCmNvZGVzeXN0ZW0gIkxPSU5DIjogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gIlNOT01FRENUIjogJ2h0dHA6Ly9zbm9tZWQuaW5mby9zY3QnCmNvZGVzeXN0ZW0gIlJvbGVDb2RlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vdjMtUm9sZUNvZGUnCmNvZGVzeXN0ZW0gIkRpYWdub3NpcyBSb2xlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUnCmNvZGVzeXN0ZW0gIlJlcXVlc3RJbnRlbnQiOiAnaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9yZXF1ZXN0LWludGVudCcKY29kZXN5c3RlbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL21lZGljYXRpb25yZXF1ZXN0LWNhdGVnb3J5Jwpjb2Rlc3lzdGVtICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2NvbmRpdGlvbi12ZXItc3RhdHVzJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24nCgpjb2RlICJCaXJ0aGRhdGUiOiAnMjExMTItOCcgZnJvbSAiTE9JTkMiIGRpc3BsYXkgJ0JpcnRoIGRhdGUnCmNvZGUgIkRlYWQiOiAnNDE5MDk5MDA5JyBmcm9tICJTTk9NRURDVCIgZGlzcGxheSAnRGVhZCcKY29kZSAiRVIiOiAnRVInIGZyb20gIlJvbGVDb2RlIiBkaXNwbGF5ICdFbWVyZ2VuY3kgcm9vbScKY29kZSAiSUNVIjogJ0lDVScgZnJvbSAiUm9sZUNvZGUiIGRpc3BsYXkgJ0ludGVuc2l2ZSBjYXJlIHVuaXQnCmNvZGUgIkJpbGxpbmciOiAnYmlsbGluZycgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0JpbGxpbmcnCgovLyBDb25kaXRpb24gQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWN0aXZlIjogJ2FjdGl2ZScgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKY29kZSAicmVjdXJyZW5jZSI6ICdyZWN1cnJlbmNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZWxhcHNlIjogJ3JlbGFwc2UnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCmNvZGUgImluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZW1pc3Npb24iOiAncmVtaXNzaW9uJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZXNvbHZlZCI6ICdyZXNvbHZlZCcgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKCi8vIENvbmRpdGlvbiBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAidW5jb25maXJtZWQiOiAndW5jb25maXJtZWQnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAicHJvdmlzaW9uYWwiOiAncHJvdmlzaW9uYWwnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiZGlmZmVyZW50aWFsIjogJ2RpZmZlcmVudGlhbCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJjb25maXJtZWQiOiAnY29uZmlybWVkJyBmcm9tIENvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzCmNvZGUgInJlZnV0ZWQiOiAncmVmdXRlZCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJlbnRlcmVkLWluLWVycm9yIjogJ2VudGVyZWQtaW4tZXJyb3InIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIEFsbGVyZ3kvSW50b2xlcmFuY2UgQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS1hY3RpdmUiOiAnYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LWluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LXJlc29sdmVkIjogJ3Jlc29sdmVkJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgoKLy8gQWxsZXJneS9JbnRvbGVyYW5jZSBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS11bmNvbmZpcm1lZCI6ICd1bmNvbmZpcm1lZCcgZnJvbSBBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJhbGxlcmd5LWNvbmZpcm1lZCI6ICdjb25maXJtZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiYWxsZXJneS1yZWZ1dGVkIjogJ3JlZnV0ZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIE1lZGljYXRpb25SZXF1ZXN0IENhdGVnb3J5IENvZGVzCmNvZGUgIkNvbW11bml0eSI6ICdjb21tdW5pdHknIGZyb20gIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiIGRpc3BsYXkgJ0NvbW11bml0eScKY29kZSAiRGlzY2hhcmdlIjogJ2Rpc2NoYXJnZScgZnJvbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgZGlzcGxheSAnRGlzY2hhcmdlJwoKLy8gRGlhZ25vc2lzIFJvbGUgQ29kZXMKY29kZSAiQUQiOiAnQUQnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdBZG1pc3Npb24gZGlhZ25vc2lzJwpjb2RlICJERCI6ICdERCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0Rpc2NoYXJnZSBkaWFnbm9zaXMnCmNvZGUgIkNDIjogJ0NDJyBmcm9tICJEaWFnbm9zaXMgUm9sZSIgZGlzcGxheSAnQ2hpZWYgY29tcGxhaW50Jwpjb2RlICJDTSI6ICdDTScgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0NvbW9yYmlkaXR5IGRpYWdub3NpcycKY29kZSAicHJlLW9wIjogJ3ByZS1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3ByZS1vcCBkaWFnbm9zaXMnCmNvZGUgInBvc3Qtb3AiOiAncG9zdC1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3Bvc3Qtb3AgZGlhZ25vc2lzJwpjb2RlICJiaWxsaW5nIjogJ2JpbGxpbmcnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdiaWxsaW5nIGRpYWdub3NpcycKCmNvbnRleHQgUGF0aWVudAoKCi8qCkBkZXNjcmlwdGlvbjogTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsCkBjb21tZW50OiBOb3JtYWxpemVzIGEgY2hvaWNlIHR5cGUgb2YgRkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbmNlLCBGSElSLnN0cmluZywgRkhJUi5BZ2UsIG9yIEZISVIuUmFuZ2UgdHlwZXMKdG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlCnJlcHJlc2VudGF0aW9ucyBmb3IgdGltaW5nLXZhbHVlZCBlbGVtZW50cyBpbiBGSElSLCBhbGxvd2luZyB0aGlzIGZ1bmN0aW9uIHRvIGJlIHVzZWQgYWNyb3NzIGFueSByZXNvdXJjZS4gTk9URTogRHVlIHRvIHRoZQpjb21wbGV4aXR5IG9mIGRldGVybWluaW5nIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgb3IgU3RyaW5nIHR5cGUsIHRoaXMgZnVuY3Rpb24gd2lsbCB0aHJvdyBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiBpdCBpcyB1c2VkCndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4KKi8KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwoY2hvaWNlIENob2ljZTxGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFudCwgRkhJUi5zdHJpbmcsIEZISVIuQWdlLCBGSElSLlJhbmdlPik6CiAgY2FzZQoJICB3aGVuIGNob2ljZSBpcyBGSElSLmRhdGVUaW1lIHRoZW4KICAgIAlJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNob2ljZSBhcyBGSElSLmRhdGVUaW1lKSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5kYXRlVGltZSldCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5QZXJpb2QgdGhlbgogIAkJRkhJUkhlbHBlcnMuVG9JbnRlcnZhbChjaG9pY2UgYXMgRkhJUi5QZXJpb2QpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5pbnN0YW50IHRoZW4KCQkJSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KV0KCQl3aGVuIGNob2ljZSBpcyBGSElSLkFnZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjaG9pY2UgYXMgRkhJUi5BZ2UpLAoJCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNob2ljZSBhcyBGSElSLkFnZSkgKyAxIHllYXIpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5SYW5nZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSgoY2hvaWNlIGFzIEZISVIuUmFuZ2UpLmxvdyksCgkJCSAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNob2ljZSBhcyBGSElSLlJhbmdlKS5oaWdoKSArIDEgeWVhcikKICAgIHdoZW4gY2hvaWNlIGlzIEZISVIuVGltaW5nIHRoZW4KCQkgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZScpCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLnN0cmluZyB0aGVuCiAgICAgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUnKQoJCWVsc2UKICAgICAgbnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4KCWVuZAoKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhIGxpc3Qgb2YgaW50ZXJ2YWxzIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBFdmVudCBvciBCb3VuZHMgb2YgYSBUaW1pbmcgcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuCiovCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbEZyb21MaXN0KHZhbHVlIExpc3Q8RkhJUi5kYXRlVGltZT4pOgogIHZhbHVlIHYKICAgIHJldHVybiBUb0ludGVydmFsKHYpCgpkZWZpbmUgZnVuY3Rpb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh2YWx1ZSBMaXN0PEludGVydmFsPERhdGVUaW1lPj4pOgogIGlmIHZhbHVlIGlzIG5vdCBudWxsIHRoZW4gdmFsdWUgZWxzZSB7IH0KCmRlZmluZSBmdW5jdGlvbiBUb1RpbWluZ0ludGVydmFsKHRpbWluZyBGSElSLlRpbWluZyk6CiAgY29udmVydCAoCiAgICBOdWxsVG9FbXB0eUludGVydmFsKFRvSW50ZXJ2YWxGcm9tTGlzdCh0aW1pbmcuZXZlbnQpKQogICAgdW5pb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh7VG9JbnRlcnZhbCh0aW1pbmcucmVwZWF0LmJvdW5kcyBhcyBGSElSLlBlcmlvZCl9KQogICkgdG8gTGlzdDxJbnRlcnZhbDxEYXRlVGltZT4+IGV4Y2VwdCB7IG51bGwgfQoKCi8qCkBkZXNjcmlwdGlvbjogcmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLgpAY29tbWVudDogTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmUKTk9URTogRnVuY3Rpb24gb3ZlcmxvYWRzIGluY29uc2lzdGVudGx5IGVyciBpbiBDUUwgMS41LCB0aGlzIGlzIGNvbW1lbnRlZCB1bnRpbCBhIGZpeCBpcyBmb3VuZApkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKToKICBvYnNlcnZhdGlvbiBvCiAgICByZXR1cm4gQ29hbGVzY2UoVG9JbnRlcnZhbChvLmVmZmVjdGl2ZSksIFRvSW50ZXJ2YWwoby5pc3N1ZWQpKQoqLwoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93CmEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9BYmF0ZW1lbnRJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBpZiBjb25kaXRpb24ub25zZXQgaXMgbm90IG51bGwgYW5kIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgbnVsbCBhbmQgKGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJhY3RpdmUiCiAgICBvciBjb25kaXRpb24uY2xpbmljYWxTdGF0dXMgfiAicmVjdXJyZW5jZSIKICAgIG9yIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJyZWxhcHNlIikgdGhlbgogICAgSW50ZXJ2YWxbTm93KCksIE5vdygpXQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmRhdGVUaW1lIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuZGF0ZVRpbWUpLCBGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5kYXRlVGltZSldCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5QZXJpb2QgdGhlbgoJICBGSElSSGVscGVycy5Ub0ludGVydmFsKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5QZXJpb2QpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5zdHJpbmcgdGhlbgogICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJzEnLCAnRXJyb3InLCAnQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZScpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5BZ2UgdGhlbgoJCUludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5BZ2UpLAoJCQlGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuQWdlKSArIDEgeWVhcikKCWVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLlJhbmdlIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5SYW5nZSkubG93KSwKCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KChjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuUmFuZ2UpLmhpZ2gpICsgMSB5ZWFyKQoJZWxzZSBpZiBjb25kaXRpb24uYWJhdGVtZW50IGlzIEZISVIuYm9vbGVhbiB0aGVuCgkgIEludGVydmFsW2VuZCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGNvbmRpdGlvbi5yZWNvcmRlZERhdGUpCgllbHNlIG51bGwKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbQpvbnNldCB0byBhYmF0ZW1lbnQgYXMgc3BlY2lmaWVkIGluIHRoZSBnaXZlbiBDb25kaXRpb24uCiovCmRlZmluZSBmdW5jdGlvbiBUb1ByZXZhbGVuY2VJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBJbnRlcnZhbFtzdGFydCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGVuZCBvZiBUb0FiYXRlbWVudEludGVydmFsKGNvbmRpdGlvbikpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4KQGNvbW1lbnQ6IFVzZXMgdGhlIFRvSW50ZXJ2YWwgZnVuY3Rpb24gYW5kIGxhc3RPY2N1cmVuY2UgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9QcmV2YWxlbmNlSW50ZXJ2YWwoYWxsZXJneUludG9sZXJhbmNlIEFsbGVyZ3lJbnRvbGVyYW5jZSk6CiAgSW50ZXJ2YWxbc3RhcnQgb2YgVG9JbnRlcnZhbChhbGxlcmd5SW50b2xlcmFuY2Uub25zZXQpLCBDb2FsZXNjZShhbGxlcmd5SW50b2xlcmFuY2UubGFzdE9jY3VycmVuY2UsIE5vdygpKV0KCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlCkNRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBFeHRlbnNpb24oZG9tYWluUmVzb3VyY2UgRG9tYWluUmVzb3VyY2UsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tICJFeHRlbnNpb25zIihkb21haW5SZXNvdXJjZSwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIGVsZW1lbnQuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEV4dGVuc2lvbnMoZWxlbWVudCwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZQpDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UubW9kaWZpZXJFeHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9IHVybAoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCB1cmwgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBNb2RpZmllckV4dGVuc2lvbnMoZG9tYWluUmVzb3VyY2UsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gdXJsCgkJcmV0dXJuIEUKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBNb2RpZmllckV4dGVuc2lvbihlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gTW9kaWZpZXJFeHRlbnNpb25zKGVsZW1lbnQsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MKY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5leHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9ICgnaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLycgKyBpZCkKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VFeHRlbnNpb25zKGVsZW1lbnQgRWxlbWVudCwgaWQgU3RyaW5nKToKICBlbGVtZW50LmV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZUV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIGlkIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gQmFzZUV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbihkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBCYXNlTW9kaWZpZXJFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCBCYWNrYm9uZUVsZW1lbnQsIGlkIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9uKGVsZW1lbnQgQmFja2JvbmVFbGVtZW50LCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/FHIRCommon" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": "CMSTest-patient-1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2028-9", + "display": "Asian" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2135-2", + "display": "Hispanic or Latino" + } + } + ] + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR", + "display": "Medical Record Number" + } + ] + }, + "system": "http://hospital.smarthealthit.org", + "value": "999999992" + } + ], + "name": [ + { + "family": "Dere", + "given": [ + "Ben" + ] + } + ], + "gender": "male", + "birthDate": "1965-01-01" + }, + "request": { + "method": "PUT", + "url": "Patient/CMSTest-patient-1" + } + } + ] } diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 0c3d42fc4cc..ea46e88b227 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 5601fd23345..be20e2d7ed8 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 2aa2bc719cf..6770994f0d3 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-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 a39eace08ee..69722db4be2 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index c4ab317ef18..a2fdb377fe7 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 3c825ba7b51..ef95578aab5 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 - 6.11.0-SNAPSHOT + 6.11.1-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 60b5e8e6a2e..2111dfa959e 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 9487e0d0ff0..67e252f4387 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 6c2a7425302..819e3e82a34 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index e494c7efe26..03ab2cdb626 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 45c1e7cfe03..10ce616b5e7 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 20f23bbf0a8..2ddd1a85eab 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 4d76e1727f6..f0bffc2308c 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 - 6.11.0-SNAPSHOT + 6.11.1-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 f6c0be183a7..e03891aa0c6 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 - 6.11.0-SNAPSHOT + 6.11.1-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 b2922d6fab3..fb09d678679 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 - 6.11.0-SNAPSHOT + 6.11.1-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 e1e486f4809..fe683d25136 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 - 6.11.0-SNAPSHOT + 6.11.1-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 b5fd584064b..1eb14c3ae13 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 - 6.11.0-SNAPSHOT + 6.11.1-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 e800a155f0c..de048b747a5 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index e4ec72d25c0..f53d993ec4f 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index abed68a4f3d..bf0e98e04f9 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 4e0ffaf8fcf..2a483fdd801 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index d3ed41f8e6e..073e576049a 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. @@ -983,7 +983,7 @@ 1.0.8 - 3.0.0-PRE9 + 3.0.0-PRE12 5.4.1 @@ -1179,11 +1179,6 @@ javax.activation ${activation_api_version} - - com.sun.activation - jakarta.activation - 2.0.0 - com.tngtech.archunit @@ -1288,6 +1283,11 @@ jakarta.annotation-api 1.3.5 + + jakarta.activation + jakarta.activation-api + 2.1.0 + jakarta.transaction jakarta.transaction-api @@ -1738,6 +1738,13 @@ org.glassfish.jaxb jaxb-runtime ${jaxb_runtime_version} + + + + com.sun.activation + jakarta.activation + + org.glassfish.jersey.core @@ -2192,6 +2199,12 @@ org.ogce xpp3 1.1.6 + + + junit + junt + + org.flywaydb diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index b8861e78f3d..a2c4f642858 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 - 6.11.0-SNAPSHOT + 6.11.1-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 6045b9eae14..ddcd9410f57 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 - 6.11.0-SNAPSHOT + 6.11.1-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 ddcf6e02280..2aca947a898 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 - 6.11.0-SNAPSHOT + 6.11.1-SNAPSHOT ../../pom.xml From f1adacf82717605da0cb0dcd9fe57c17bbbe30eb Mon Sep 17 00:00:00 2001 From: Martha Mitran Date: Wed, 22 Nov 2023 13:18:03 -0800 Subject: [PATCH 39/86] Add package installer property resource filter on status (#5470) * Expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios with a constraint violation exception when codes (term concepts) were being persisted * Expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios with a constraint violation exception when codes (term concepts) were being persisted * Small changes after code review. Doing some variable/method renaming and correcting changelog Jira issue number. * There is a programmatic filter enabled which skips installation for certain resources based on their status in PackageInstallerSvcImpl. The filter can now be controlled via StorageSettings. * Fix typo in variable name from recent pull request 5461. --- ...er-property-filter-resource-on-status.yaml | 6 + .../jpa/packages/PackageInstallerSvcImpl.java | 12 +- .../packages/PackageInstallerSvcImplTest.java | 184 ++++++++---------- .../jpa/model/entity/StorageSettings.java | 24 +++ .../ValueSetExpansionWithHierarchyR4Test.java | 4 +- 5 files changed, 125 insertions(+), 105 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml new file mode 100644 index 00000000000..1e998f245db --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5469 +jira: SMILE-7594 +title: "There is a programmatic filter enabled which skips installation for certain resources based on their status +in PackageInstallerSvcImpl. The filter can now be controlled via StorageSettings." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index bc7fe8f5f34..6b09789cca6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; @@ -72,7 +73,6 @@ import javax.annotation.PostConstruct; import static ca.uhn.fhir.jpa.packages.util.PackageUtils.DEFAULT_INSTALL_TYPES; import static ca.uhn.fhir.util.SearchParameterUtil.getBaseAsStrings; -import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; /** @@ -114,6 +114,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { @Autowired private PackageResourceParsingSvc myPackageResourceParsingSvc; + @Autowired + private JpaStorageSettings myStorageSettings; + /** * Constructor */ @@ -506,7 +509,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { if ("SearchParameter".equals(resourceType)) { String code = SearchParameterUtil.getCode(myFhirContext, theResource); - if (defaultString(code).startsWith("_")) { + if (!isBlank(code) && code.startsWith("_")) { ourLog.warn( "Failed to validate resource of type {} with url {} - Error: Resource code starts with \"_\"", theResource.fhirType(), @@ -548,7 +551,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * and {@link org.hl7.fhir.r4.model.Communication}, the status field doesn't necessarily need to be set to 'active' * for that resource to be eligible for upload via packages. For example, all {@link org.hl7.fhir.r4.model.Subscription} * have a status of {@link org.hl7.fhir.r4.model.Subscription.SubscriptionStatus#REQUESTED} when they are originally - * inserted into the database, so we accept that value for {@link org.hl7.fhir.r4.model.Subscription} isntead. + * inserted into the database, so we accept that value for {@link org.hl7.fhir.r4.model.Subscription} instead. * Furthermore, {@link org.hl7.fhir.r4.model.DocumentReference} and {@link org.hl7.fhir.r4.model.Communication} can * exist with a wide variety of values for status that include ones such as * {@link org.hl7.fhir.r4.model.Communication.CommunicationStatus#ENTEREDINERROR}, @@ -560,6 +563,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * @return {@link Boolean#TRUE} if the status value of this resource is acceptable for package upload. */ private boolean isValidResourceStatusForPackageUpload(IBaseResource theResource) { + if (!myStorageSettings.isValidateResourceStatusForPackageUpload()) { + return true; + } List statusTypes = myFhirContext.newFhirPath().evaluate(theResource, "status", IPrimitiveType.class); // Resource does not have a status field diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java index df28a3f2689..75e8cee5e15 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; @@ -26,9 +27,11 @@ import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageGenerator; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -46,10 +49,10 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -78,6 +81,8 @@ public class PackageInstallerSvcImplTest { private SearchParameterHelper mySearchParameterHelper; @Mock private SearchParameterMap mySearchParameterMap; + @Mock + private JpaStorageSettings myStorageSettings; @Spy private FhirContext myCtx = FhirContext.forR4Cached(); @Spy @@ -103,111 +108,66 @@ public class PackageInstallerSvcImplTest { mySvc.assertFhirVersionsAreCompatible("R4", "R4B"); } - @Test - public void testValidForUpload_SearchParameterWithMetaParam() { - SearchParameter sp = new SearchParameter(); - sp.setCode("_id"); - assertFalse(mySvc.validForUpload(sp)); - } + @Nested + class ValidForUploadTest { + public static Stream parametersIsValidForUpload() { + SearchParameter sp1 = new SearchParameter(); + sp1.setCode("_id"); - @Test - public void testValidForUpload_SearchParameterWithNoBase() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.setExpression("Patient.name"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertFalse(mySvc.validForUpload(sp)); - } + SearchParameter sp2 = new SearchParameter(); + sp2.setCode("name"); + sp2.setExpression("Patient.name"); + sp2.setStatus(Enumerations.PublicationStatus.ACTIVE); - @Test - public void testValidForUpload_SearchParameterWithNoExpression() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.addBase("Patient"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertFalse(mySvc.validForUpload(sp)); - } + SearchParameter sp3 = new SearchParameter(); + sp3.setCode("name"); + sp3.addBase("Patient"); + sp3.setStatus(Enumerations.PublicationStatus.ACTIVE); + SearchParameter sp4 = new SearchParameter(); + sp4.setCode("name"); + sp4.addBase("Patient"); + sp4.setExpression("Patient.name"); + sp4.setStatus(Enumerations.PublicationStatus.ACTIVE); - @Test - public void testValidForUpload_GoodSearchParameter() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.addBase("Patient"); - sp.setExpression("Patient.name"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertTrue(mySvc.validForUpload(sp)); - } + SearchParameter sp5 = new SearchParameter(); + sp5.setCode("name"); + sp5.addBase("Patient"); + sp5.setExpression("Patient.name"); + sp5.setStatus(Enumerations.PublicationStatus.DRAFT); - @Test - public void testValidForUpload_RequestedSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); - assertTrue(mySvc.validForUpload(subscription)); - } + return Stream.of( + arguments(sp1, false, false), + arguments(sp2, false, true), + arguments(sp3, false, true), + arguments(sp4, true, true), + arguments(sp5, true, false), + arguments(createSubscription(Subscription.SubscriptionStatus.REQUESTED), true, true), + arguments(createSubscription(Subscription.SubscriptionStatus.ERROR), true, false), + arguments(createSubscription(Subscription.SubscriptionStatus.ACTIVE), true, false), + arguments(createDocumentReference(Enumerations.DocumentReferenceStatus.ENTEREDINERROR), true, true), + arguments(createDocumentReference(Enumerations.DocumentReferenceStatus.NULL), true, false), + arguments(createDocumentReference(null), true, false), + arguments(createCommunication(Communication.CommunicationStatus.NOTDONE), true, true), + arguments(createCommunication(Communication.CommunicationStatus.NULL), true, false), + arguments(createCommunication(null), true, false)); + } - @Test - public void testValidForUpload_ErrorSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.ERROR); - assertFalse(mySvc.validForUpload(subscription)); - } + @ParameterizedTest + @MethodSource(value = "parametersIsValidForUpload") + public void testValidForUpload_withResource(IBaseResource theResource, + boolean theTheMeetsOtherFilterCriteria, + boolean theMeetsStatusFilterCriteria) { + if (theTheMeetsOtherFilterCriteria) { + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); + } + assertEquals(theTheMeetsOtherFilterCriteria && theMeetsStatusFilterCriteria, mySvc.validForUpload(theResource)); - @Test - public void testValidForUpload_ActiveSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE); - assertFalse(mySvc.validForUpload(subscription)); - } - - @Test - public void testValidForUpload_DocumentRefStatusValuePresent() { - DocumentReference documentReference = new DocumentReference(); - documentReference.setStatus(Enumerations.DocumentReferenceStatus.ENTEREDINERROR); - assertTrue(mySvc.validForUpload(documentReference)); - } - - @Test - public void testValidForUpload_DocumentRefStatusValueNull() { - DocumentReference documentReference = new DocumentReference(); - documentReference.setStatus(Enumerations.DocumentReferenceStatus.NULL); - assertFalse(mySvc.validForUpload(documentReference)); - documentReference.setStatus(null); - assertFalse(mySvc.validForUpload(documentReference)); - } - - @Test - public void testValidForUpload_CommunicationStatusValuePresent() { - Communication communication = new Communication(); - communication.setStatus(Communication.CommunicationStatus.NOTDONE); - assertTrue(mySvc.validForUpload(communication)); - } - - @Test - public void testValidForUpload_CommunicationStatusValueNull() { - Communication communication = new Communication(); - communication.setStatus(Communication.CommunicationStatus.NULL); - assertFalse(mySvc.validForUpload(communication)); - communication.setStatus(null); - assertFalse(mySvc.validForUpload(communication)); + if (theTheMeetsOtherFilterCriteria) { + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(false); + } + assertEquals(theTheMeetsOtherFilterCriteria, mySvc.validForUpload(theResource)); + } } @Test @@ -346,4 +306,28 @@ public class PackageInstallerSvcImplTest { searchParameter.setExpression("someExpression"); return searchParameter; } + + private static Subscription createSubscription(Subscription.SubscriptionStatus theSubscriptionStatus) { + Subscription.SubscriptionChannelComponent subscriptionChannelComponent = + new Subscription.SubscriptionChannelComponent() + .setType(Subscription.SubscriptionChannelType.RESTHOOK) + .setEndpoint("https://tinyurl.com/2p95e27r"); + Subscription subscription = new Subscription(); + subscription.setCriteria("Patient?name=smith"); + subscription.setChannel(subscriptionChannelComponent); + subscription.setStatus(theSubscriptionStatus); + return subscription; + } + + private static DocumentReference createDocumentReference(Enumerations.DocumentReferenceStatus theDocumentStatus) { + DocumentReference documentReference = new DocumentReference(); + documentReference.setStatus(theDocumentStatus); + return documentReference; + } + + private static Communication createCommunication(Communication.CommunicationStatus theCommunicationStatus) { + Communication communication = new Communication(); + communication.setStatus(theCommunicationStatus); + return communication; + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java index b14872263ce..cf543b41520 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java @@ -146,6 +146,14 @@ public class StorageSettings { */ private boolean myLanguageSearchParameterEnabled = false; + /** + * If set to false, all resource types will be installed via package installer, regardless of their status. + * Otherwise, resources will be filtered based on status according to some criteria which can be found in + * PackageInstallerSvcImpl#isValidResourceStatusForPackageUpload + * @since 7.0.0 + */ + private boolean myValidateResourceStatusForPackageUpload = true; + /** * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve * overall server performance. @@ -1319,6 +1327,22 @@ public class StorageSettings { myLanguageSearchParameterEnabled = theLanguageSearchParameterEnabled; } + /** + * @return true if the filter is enabled for resources installed via package installer, false otherwise + * @since 7.0.0 + */ + public boolean isValidateResourceStatusForPackageUpload() { + return myValidateResourceStatusForPackageUpload; + } + + /** + * Should resources being installed via package installer be filtered. + * @since 7.0.0 + */ + public void setValidateResourceStatusForPackageUpload(boolean theValidateResourceStatusForPackageUpload) { + myValidateResourceStatusForPackageUpload = theValidateResourceStatusForPackageUpload; + } + private static void validateTreatBaseUrlsAsLocal(String theUrl) { Validate.notBlank(theUrl, "Base URL must not be null or empty"); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java index a03e78028b9..607491ec002 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java @@ -97,13 +97,13 @@ public class ValueSetExpansionWithHierarchyR4Test extends BaseTermR4Test { @ParameterizedTest @MethodSource(value = "parametersValueSets") - public void testExpandValueSet_whenUsingHierarchicalCodeSystem_willExpandSuccessfully(ValueSet theValueSet, int theExpectedConceptExpensionCount) { + public void testExpandValueSet_whenUsingHierarchicalCodeSystem_willExpandSuccessfully(ValueSet theValueSet, int theExpectedConceptExpansionCount) { myValueSetDao.create(theValueSet, mySrd); myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); Optional optionalTermValueSet = runInTransaction(() -> myTermValueSetDao.findTermValueSetByUrlAndNullVersion(theValueSet.getUrl())); assertTrue(optionalTermValueSet.isPresent()); TermValueSet expandedTermValueSet = optionalTermValueSet.get(); assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, expandedTermValueSet.getExpansionStatus()); - assertEquals(theExpectedConceptExpensionCount, expandedTermValueSet.getTotalConcepts()); + assertEquals(theExpectedConceptExpansionCount, expandedTermValueSet.getTotalConcepts()); } } From f624cf75adaa387ef0577b88ef8810745236b7a9 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 22 Nov 2023 19:01:31 -0500 Subject: [PATCH 40/86] Rel 6 10 mb (#5474) * Allow cached search with consent active when safe (#5387) Allow the search cache when using consent if safe * Change package installation behaviour such that it updates the existing SearchParameter base with remaining resources (#5376) * Change package installation behavior such that it updates the existing SearchParameter base with remaining resources * Change package installation behavior such that it updates the existing SearchParameter base with remaining resources * Use resourceType in the package installer output to fix tests. Minor change with resourceType condition. Update changelog description to make it more readable. * Use resourceType in the package installer output to fix tests. Minor change with resourceType condition. Update changelog description to make it more readable. * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning (#5389) * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning - Implementation * Reverse Chaining searches returns an error when invoked with parameter _lastUpdated. (#5177) * version bump * Bump to core release 6.0.22 (#5028) * Bump to core release 6.0.16 * Bump to core version 6.0.20 * Fix errors thrown as a result of VersionSpecificWorkerContextWrapper * Bump to core 6.0.22 * Resolve 5126 hfj res ver prov might cause migration error on db that automatically indexes the primary key (#5127) * dropped old index FK_RESVERPROV_RES_PID on RES_PID column before adding IDX_RESVERPROV_RES_PID * added changelog * changed to valid version number * changed to valid version number, need to be ordered by version number... * 5123 - Use DEFAULT partition for server-based requests if none specified (#5124) 5123 - Use DEFAULT partition for server-based requests if none specified * consent remove all suppresses next link in bundle (#5119) * added FIXME with source of issue * added FIXME with root cause * added FIXME with root cause * Providing solution to the issue and removing fixmes. * Providing changelog * auto-formatting. * Adding new test. * Adding a new test for standard paging * let's try this and see if it works...? * fix tests * cleanup to trigger a new run * fixing tests --------- Co-authored-by: Ken Stevens Co-authored-by: peartree * 5117 MDM Score for No Match Fields Should Not Be Included in Total Score (#5118) * fix, test, changelog * fix, test, changelog --------- Co-authored-by: justindar * _source search parameter needs to support modifiers (#5095) _source search parameter needs to support modifiers - added support form :contains, :missing, :above modifiers * Fix HFQL docs (#5151) * Expunge operation on codesystem may throw 500 internal error with precondition fail message. (#5156) * Initial failing test. * Solution with changelog. * fixing format. * Addressing comment from code review. * fixing failing test. --------- Co-authored-by: peartree * documentation update (#5154) Co-authored-by: leif stawnyczy * Fix hsql jdbc driver deps (#5168) Avoid non-included classes in jdbc driver dependencies. * $delete-expunge over 10k resources will now delete all resources (#5144) * First commit with very rough fix and unit test. * Refinements to ResourceIdListStep and Batch2DaoSvcImpl. Make LoadIdsStepTest pass. Enhance Batch2DaoSvcImplTest. * Spotless * Fix checkstyle errors. * Fix test failures. * Minor refactoring. New unit test. Finalize changelist. * Spotless fix. * Delete now useless code from unit test. * Delete more useless code. * Test pre-commit hook * More spotless fixes. * Address most code review feedback. * Remove use of pageSize parameter and see if this breaks the pipeline. * Remove use of pageSize parameter and see if this breaks the pipeline. * Fix the noUrl case by passing an unlimited Pegeable instead. Effectively stop using page size for most databases. * Deprecate the old method and have it call the new one by default. * updating documentation (#5170) Co-authored-by: leif stawnyczy * _source search parameter modifiers for Subscription matching (#5159) * _source search parameter modifiers for Subscription matching - test, implementation and changelog * first fix * tests and preliminary fixes * wip, commit before switching to release branch. * adding capability to handle _lastUpdated in reverse search (_has) * adding changelog * applying spotless. * addressing code review comments. --------- Co-authored-by: tadgh Co-authored-by: dotasek Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: Nathan Doef Co-authored-by: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Co-authored-by: TipzCM Co-authored-by: leif stawnyczy Co-authored-by: michaelabuckley Co-authored-by: Luke deGruchy * Br 20231019 add cr settings for cds hooks (#5394) * Add settings used in CR CDS Services. Remove config dependency on Spring Boot. * Add changelog * Use String.format rather than concat strings * spotless apply * Add javadoc * Upgrade notes for the forced-id change (#5400) Add upgrade notes for forced-id * Clean stale search results more aggressively. (#5396) Use bulk DMA statements when cleaning the search cache. The cleaner job now works as long as possible until a deadline based on the scheduling frequency. * bump version of clinical reasoning (#5406) * Transaction fails if SearchNarrowingInterceptor is registered and Partitioning Enabled - fix cross-tenant requests failure (#5408) * Transaction with conditional update fails if SearchNarrowingInterceptor is registered and Enabled Partitioning - fix and tests added * removed unused alias from SQL query of mdm-clear (#5416) * Issue 5418 support Boolean class return type in BaseInterceptorService (#5421) * Enable child classes to use Boolean class return type * spotless --------- Co-authored-by: juan.marchionatto * If AutoInflateBinaries is enabled, binaries are created on the disk only for the first resource entry of the bundle (#5420) * If AutoInflateBinaries is enabled, binaries created on disk by bundled requests are created only for the first resource entry - fix * Revert "Issue 5418 support Boolean class return type in BaseInterceptorService (#5421)" (#5423) This reverts commit 4e295a59fb143a2ba54bc44305474096e2acd578. Co-authored-by: Nathan Doef * Use new FHIR_ID column for sorting (#5405) * Sort `_id` using new FHIR_ID column. * Fix old tests that put client-assigned ids first. * Better indexing for sort * Bump core to 6.1.2.2 (#5425) * Bump core to 6.1.2.1 Patch release that uses https for primary org.hl7.fhir.core package server * Bump core to 6.1.2.2 * Make sure to return always a value for Boolean class return type. (#5424) Implement change in a non-disruptive way for overriders Co-authored-by: juan.marchionatto * Add non-standard __pid SP for breaking ties cheaply during sorts. (#5428) Add a non-standard __pid SP. * Review changes for new _pid SP. (#5430) Change name to _pid to match our standard and add warning. * Fix VersionCanonicalizer conversion from R5 into DSTU2 for CapabilityStatement, Parameters and StructuredDefinition (#5432) * Fix VersionCanonicalizer conversion from R5 into DSTU2 for CapabilityStatement, Parameters and StructuredDefinition. * Fix spotless issue * CVEs for 6.10.0 (#5433) * Bump jetty * Bump okio-jvm * 8.2.0 mysql connector * Jena and elastic bumps * Fix test * 5412 post bundle on partition incorrect response.link shown (#5413) * Initial fix and unit test provided * spottless check * Made relevant changes to make solution version agnostic * relevant logic changes made * spotless changes made * New logic added to fix failing test cases * formatting * New logic to make the function more robust * spotless checks * Left a trailing slash in the tests * Made relevant test changes and changed logic * spotless changes * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-fullUrl-not-shown-in-response.yaml changing changelog Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> * Formatting requirements --------- Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> * Resolve We don't have guaranteed subscription delivery if a resource is too large (#5414) * first fix * - added the ability to handle null payload to SubscriptionDeliveringMessageSubscriber and SubscriptionDeliveringEmailSubscriber - refactored code to reduce repeated code - cleaned unnecessary comments and reformatted files * Changed myResourceModifiedMessagePersistenceSvc to be autowired * removed unused import * added error handling when inflating the message to email and message subscriber * reformatted code * Fixing subscription tests with mocked IResourceModifiedMessagePersistenceSvc * Changes by gary * Reformatted file * fixed failed tests * implemented test for message and email delivery subscriber. Fixed logical error. Reformatted File. * - implemented IT - fixed logical error - added changelog * fix for cdr tests, NOTE: this makes the assumption that we will always succeed for inflating the database in the tests that uses SynchronousSubscriptionMatcherInterceptor * fix for cdr tests, NOTE: this makes the assumption that we will always succeed for inflating the database in the tests that uses SynchronousSubscriptionMatcherInterceptor * resolve code review comments * reformatted files * fixed tests * Fix for failing IT test in jpaserver-starter (#5435) Co-authored-by: dotasek * wip * Bump jackson databind * Pin Version * Ignored duplicate classes * Updating version to: 6.10.1 post release. * Fix pom * Skip remorte nexus * make release faster * Updating version to: 6.10.1 post release. * remove skiptests --------- Co-authored-by: michaelabuckley Co-authored-by: Martha Mitran Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Co-authored-by: dotasek Co-authored-by: Steve Corbett <137920358+steve-corbett-smilecdr@users.noreply.github.com> Co-authored-by: Ken Stevens Co-authored-by: Ken Stevens Co-authored-by: peartree Co-authored-by: jdar8 <69840459+jdar8@users.noreply.github.com> Co-authored-by: justindar Co-authored-by: Nathan Doef Co-authored-by: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Co-authored-by: TipzCM Co-authored-by: leif stawnyczy Co-authored-by: Luke deGruchy Co-authored-by: Brenin Rhodes Co-authored-by: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Co-authored-by: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Co-authored-by: juan.marchionatto Co-authored-by: Nathan Doef Co-authored-by: LalithE <132382565+LalithE@users.noreply.github.com> Co-authored-by: dotasek Co-authored-by: markiantorno --- .../src/main/java/ca/uhn/fhir/util/VersionEnum.java | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 8 ++++++++ pom.xml | 4 ++-- tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml | 8 ++++++++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index bb2d3a0146c..a923b31f050 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -129,7 +129,7 @@ public enum VersionEnum { V6_9_0, V6_10_0, - + V6_10_1, V6_11_0, V7_0_0; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index af8ccff2927..6e5a97eca04 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -235,6 +235,14 @@ + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + true + + org.apache.maven.plugins maven-deploy-plugin diff --git a/pom.xml b/pom.xml index 073e576049a..fc49e3185d9 100644 --- a/pom.xml +++ b/pom.xml @@ -950,8 +950,8 @@ 6.1.5.Final 4.4.13 4.5.13 - 2.15.2 - 2.15.2 + 2.15.3 + 2.15.3 3.3.0 1.8 4.10.0 diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index a2c4f642858..3e83bda6a90 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -104,6 +104,14 @@ hapi-fhir-jaxrsserver-example + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + true + + org.apache.maven.plugins maven-failsafe-plugin From a1aa9c4a3636966fb9e907fa08b6476a4d4a3cdf Mon Sep 17 00:00:00 2001 From: jmarchionatto <60409882+jmarchionatto@users.noreply.github.com> Date: Thu, 23 Nov 2023 12:22:05 -0500 Subject: [PATCH 41/86] Issue 5081 support mdm on version r5 (#5099) * Create SubscriptionTopic before creating Subscription for MDM loader when on R5 * Remove unused properties * MdmSubscriptionLoader changes to support R5 in MDM - added SubscriptionTopic queryCriteria (draft) * MDM not supported for R5 - implementation * MDM not supported for R5 - implementation * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fix * MDM not supported for R5 - implementation fixes, changelog * MDM not supported for R5 - fixes * MDM not supported for R5 - fixes * MDM not supported for R5 - fixes * MDM not supported for R5 - fix --------- Co-authored-by: juan.marchionatto Co-authored-by: volodymyr --- .../7_0_0/5081-add-mdm-support-for-r5.yaml | 4 + .../jpa/mdm/broker/MdmMessageHandler.java | 14 ++- .../jpa/mdm/config/MdmSubscriptionLoader.java | 85 +++++++++++++++-- .../config/MdmSubscriptionLoaderR5Test.java | 95 +++++++++++++++++++ .../jpa/topic/SubscriptionTopicConfig.java | 47 +++++++-- .../SubscriptionTopicMatchingSubscriber.java | 11 --- .../fhir/jpa/topic/SubscriptionTopicUtil.java | 27 ++++++ .../jpa/topic/SubscriptionTopicUtilTest.java | 58 +++++++++++ .../ca/uhn/fhir/mdm/api/MdmConstants.java | 1 + .../ca/uhn/fhir/mdm/model/CanonicalEID.java | 7 ++ .../fhir/mdm/provider/MdmProviderLoader.java | 1 + .../fieldmatchers/DateTimeWrapper.java | 4 + .../fhir/mdm/util/GoldenResourceHelper.java | 3 +- .../ca/uhn/fhir/mdm/util/IdentifierUtil.java | 4 +- 14 files changed, 335 insertions(+), 26 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml create mode 100644 hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml new file mode 100644 index 00000000000..c0e5e6a1b7f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5081 +title: "Added MDM support for FHIR R5." diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 32d51dec50d..9be8ec8f76a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.broker; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; @@ -30,6 +31,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicUtil; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; @@ -39,6 +41,7 @@ import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -81,7 +84,7 @@ public class MdmMessageHandler implements MessageHandler { ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload(); try { - IBaseResource sourceResource = msg.getNewPayload(myFhirContext); + IBaseResource sourceResource = extractSourceResource(msg); boolean toProcess = myMdmResourceFilteringSvc.shouldBeProcessed((IAnyResource) sourceResource); if (toProcess) { @@ -93,6 +96,15 @@ public class MdmMessageHandler implements MessageHandler { } } + private IBaseResource extractSourceResource(ResourceModifiedMessage theResourceModifiedMessage) { + IBaseResource sourceResource = theResourceModifiedMessage.getNewPayload(myFhirContext); + if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.R5 && sourceResource instanceof IBaseBundle) { + return SubscriptionTopicUtil.extractResourceFromBundle(myFhirContext, (IBaseBundle) sourceResource); + } else { + return sourceResource; + } + } + private void matchMdmAndUpdateLinks(IBaseResource theSourceResource, ResourceModifiedMessage theMsg) { String resourceType = theSourceResource.getIdElement().getResourceType(); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java index 4cf52bb8f8a..5e665205acf 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java @@ -27,9 +27,12 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicLoader; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.log.Logs; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -37,17 +40,21 @@ import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Subscription; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.Enumerations; +import org.hl7.fhir.r5.model.SubscriptionTopic; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Service public class MdmSubscriptionLoader { - public static final String MDM_SUBSCIPRION_ID_PREFIX = "mdm-"; + public static final String MDM_SUBSCRIPTION_ID_PREFIX = "mdm-"; private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired @@ -65,7 +72,11 @@ public class MdmSubscriptionLoader { @Autowired private IMdmSettings myMdmSettings; + @Autowired(required = false) + private SubscriptionTopicLoader mySubscriptionTopicLoader; + private IFhirResourceDao mySubscriptionDao; + private IFhirResourceDao mySubscriptionTopicDao; public synchronized void daoUpdateMdmSubscriptions() { List subscriptions; @@ -73,16 +84,24 @@ public class MdmSubscriptionLoader { switch (myFhirContext.getVersion().getVersion()) { case DSTU3: subscriptions = mdmResourceTypes.stream() - .map(resourceType -> - buildMdmSubscriptionDstu3(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType + "?")) + .map(resourceType -> buildMdmSubscriptionDstu3( + MDM_SUBSCRIPTION_ID_PREFIX + resourceType, resourceType + "?")) .collect(Collectors.toList()); break; case R4: subscriptions = mdmResourceTypes.stream() .map(resourceType -> - buildMdmSubscriptionR4(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType + "?")) + buildMdmSubscriptionR4(MDM_SUBSCRIPTION_ID_PREFIX + resourceType, resourceType + "?")) .collect(Collectors.toList()); break; + case R5: + SubscriptionTopic subscriptionTopic = buildMdmSubscriptionTopicR5(mdmResourceTypes); + updateSubscriptionTopic(subscriptionTopic); + // After loading subscriptionTopic, sync subscriptionTopic to the registry. + mySubscriptionTopicLoader.syncDatabaseToCache(); + + subscriptions = buildMdmSubscriptionR5(subscriptionTopic); + break; default: throw new ConfigurationException(Msg.code(736) + "MDM not supported for FHIR version " + myFhirContext.getVersion().getVersion()); @@ -107,6 +126,11 @@ public class MdmSubscriptionLoader { } } + synchronized void updateSubscriptionTopic(SubscriptionTopic theSubscriptionTopic) { + mySubscriptionTopicDao = myDaoRegistry.getResourceDao("SubscriptionTopic"); + mySubscriptionTopicDao.update(theSubscriptionTopic, SystemRequestDetails.forAllPartitions()); + } + private org.hl7.fhir.dstu3.model.Subscription buildMdmSubscriptionDstu3(String theId, String theCriteria) { org.hl7.fhir.dstu3.model.Subscription retval = new org.hl7.fhir.dstu3.model.Subscription(); retval.setId(theId); @@ -124,7 +148,7 @@ public class MdmSubscriptionLoader { channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); - channel.setPayload("application/json"); + channel.setPayload(Constants.CT_JSON); return retval; } @@ -145,7 +169,56 @@ public class MdmSubscriptionLoader { channel.setType(Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); - channel.setPayload("application/json"); + channel.setPayload(Constants.CT_JSON); return retval; } + + private SubscriptionTopic buildMdmSubscriptionTopicR5(List theMdmResourceTypes) { + SubscriptionTopic subscriptionTopic = new SubscriptionTopic(); + subscriptionTopic.setId(MDM_SUBSCRIPTION_ID_PREFIX + "subscription-topic"); + subscriptionTopic + .getMeta() + .addTag() + .setSystem(MdmConstants.SYSTEM_MDM_MANAGED) + .setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + subscriptionTopic.setStatus(Enumerations.PublicationStatus.ACTIVE); + subscriptionTopic.setUrl(MdmConstants.SUBSCRIPTION_TOPIC_URL); + theMdmResourceTypes.forEach( + resourceType -> buildSubscriptionTopicResourceTriggerComponent(resourceType, subscriptionTopic)); + return subscriptionTopic; + } + + private static void buildSubscriptionTopicResourceTriggerComponent( + String theResourceType, SubscriptionTopic theSubscriptionTopic) { + SubscriptionTopic.SubscriptionTopicResourceTriggerComponent triggerComponent = + theSubscriptionTopic.addResourceTrigger(); + triggerComponent.setResource(theResourceType); + triggerComponent.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.CREATE); + triggerComponent.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE); + } + + private List buildMdmSubscriptionR5(SubscriptionTopic theSubscriptionTopic) { + org.hl7.fhir.r5.model.Subscription subscription = new org.hl7.fhir.r5.model.Subscription(); + + subscription.setId(MDM_SUBSCRIPTION_ID_PREFIX + "subscription"); + subscription.setReason("MDM"); + subscription.setStatus(Enumerations.SubscriptionStatusCodes.REQUESTED); + + subscription.setTopic(theSubscriptionTopic.getUrl()); + subscription + .getMeta() + .addTag() + .setSystem(MdmConstants.SYSTEM_MDM_MANAGED) + .setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + + subscription.setChannelType(new Coding() + .setCode(CanonicalSubscriptionChannelType.MESSAGE.toCode()) + .setSystem(CanonicalSubscriptionChannelType.MESSAGE.getSystem())); + + subscription.setEndpoint("channel:" + + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); + subscription.setContentType(Constants.CT_JSON); + + return Collections.singletonList(subscription); + } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java new file mode 100644 index 00000000000..88f106c261a --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java @@ -0,0 +1,95 @@ +package ca.uhn.fhir.jpa.mdm.config; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; +import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicLoader; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Subscription; +import org.hl7.fhir.r5.model.SubscriptionTopic; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class MdmSubscriptionLoaderR5Test { + + @Mock + IFhirResourceDao mySubscriptionDao; + @Mock + IFhirResourceDao mySubscriptionTopicDao; + @Mock + DaoRegistry myDaoRegistry; + @Mock + IMdmSettings myMdmSettings; + @Spy + FhirContext myFhirContext = FhirContext.forR5Cached(); + @Mock + IChannelNamer myChannelNamer; + @Mock + SubscriptionLoader mySubscriptionLoader; + @Mock + SubscriptionTopicLoader mySubscriptionTopicLoader; + @InjectMocks + MdmSubscriptionLoader mySvc = new MdmSubscriptionLoader(); + + @AfterEach + public void after() { + verifyNoMoreInteractions(mySubscriptionTopicDao); + } + + @Test + public void testDaoUpdateMdmSubscriptions_withR5FhirContext_createsCorrectSubscriptions() { + // setup + MdmRulesJson mdmRulesJson = new MdmRulesJson(); + mdmRulesJson.setMdmTypes(Arrays.asList("Patient")); + when(myMdmSettings.getMdmRules()).thenReturn(mdmRulesJson); + when(myChannelNamer.getChannelName(any(), any())).thenReturn("Test"); + when(myDaoRegistry.getResourceDao(eq("Subscription"))).thenReturn(mySubscriptionDao); + when(myDaoRegistry.getResourceDao(eq("SubscriptionTopic"))).thenReturn(mySubscriptionTopicDao); + when(mySubscriptionDao.read(any(), any(RequestDetails.class))).thenThrow(new ResourceGoneException("")); + + // execute + mySvc.daoUpdateMdmSubscriptions(); + + // verify SubscriptionTopic + ArgumentCaptor subscriptionTopicCaptor = ArgumentCaptor.forClass(SubscriptionTopic.class); + verify(mySubscriptionTopicDao).update(subscriptionTopicCaptor.capture(), any(RequestDetails.class)); + + SubscriptionTopic subscriptionTopic = subscriptionTopicCaptor.getValue(); + assertNotNull(subscriptionTopic); + assertEquals("mdm-subscription-topic", subscriptionTopic.getId()); + assertEquals(1, subscriptionTopic.getResourceTrigger().size()); + SubscriptionTopic.SubscriptionTopicResourceTriggerComponent triggerComponent = subscriptionTopic.getResourceTrigger().get(0); + assertEquals("Patient", triggerComponent.getResource()); + + // verify Subscription + ArgumentCaptor subscriptionCaptor = ArgumentCaptor.forClass(Subscription.class); + verify(mySubscriptionDao).update(subscriptionCaptor.capture(), any(RequestDetails.class)); + + Subscription subscription = subscriptionCaptor.getValue(); + assertNotNull(subscription); + assertEquals("mdm-subscription", subscription.getId()); + } +} diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java index 6e53878e4e4..c1c0dbbf8e8 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java @@ -22,39 +22,74 @@ package ca.uhn.fhir.jpa.topic; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher; +import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator; import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; public class SubscriptionTopicConfig { @Bean SubscriptionTopicMatchingSubscriber subscriptionTopicMatchingSubscriber(FhirContext theFhirContext) { - return new SubscriptionTopicMatchingSubscriber(theFhirContext); + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicMatchingSubscriber(theFhirContext); + default: + return null; + } } @Bean + @Lazy SubscriptionTopicRegistry subscriptionTopicRegistry() { return new SubscriptionTopicRegistry(); } @Bean + @Lazy SubscriptionTopicSupport subscriptionTopicSupport( FhirContext theFhirContext, DaoRegistry theDaoRegistry, SearchParamMatcher theSearchParamMatcher) { return new SubscriptionTopicSupport(theFhirContext, theDaoRegistry, theSearchParamMatcher); } @Bean - SubscriptionTopicLoader subscriptionTopicLoader() { - return new SubscriptionTopicLoader(); + SubscriptionTopicLoader subscriptionTopicLoader(FhirContext theFhirContext) { + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicLoader(); + default: + return null; + } } @Bean - SubscriptionTopicRegisteringSubscriber subscriptionTopicRegisteringSubscriber() { - return new SubscriptionTopicRegisteringSubscriber(); + SubscriptionTopicRegisteringSubscriber subscriptionTopicRegisteringSubscriber(FhirContext theFhirContext) { + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicRegisteringSubscriber(); + default: + return null; + } + } + + @Bean + @Lazy + public SubscriptionQueryValidator subscriptionQueryValidator( + DaoRegistry theDaoRegistry, SubscriptionStrategyEvaluator theSubscriptionStrategyEvaluator) { + return new SubscriptionQueryValidator(theDaoRegistry, theSubscriptionStrategyEvaluator); } @Bean SubscriptionTopicValidatingInterceptor subscriptionTopicValidatingInterceptor( FhirContext theFhirContext, SubscriptionQueryValidator theSubscriptionQueryValidator) { - return new SubscriptionTopicValidatingInterceptor(theFhirContext, theSubscriptionQueryValidator); + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicValidatingInterceptor(theFhirContext, theSubscriptionQueryValidator); + default: + return null; + } } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java index 758b2389078..abccbe2c514 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java @@ -24,8 +24,6 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; -import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchDeliverer; -import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.topic.filter.InMemoryTopicFilterMatcher; @@ -57,15 +55,6 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { @Autowired SubscriptionTopicRegistry mySubscriptionTopicRegistry; - @Autowired - SubscriptionRegistry mySubscriptionRegistry; - - @Autowired - SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; - - @Autowired - SubscriptionTopicPayloadBuilder mySubscriptionTopicPayloadBuilder; - @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java index 7d863fc077c..0a05e8f44aa 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java @@ -19,11 +19,19 @@ */ package ca.uhn.fhir.jpa.topic; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage; +import ca.uhn.fhir.util.BundleUtil; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.BaseReference; import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.SubscriptionStatus; import org.hl7.fhir.r5.model.SubscriptionTopic; import java.util.List; +import java.util.Objects; public class SubscriptionTopicUtil { public static boolean matches( @@ -45,4 +53,23 @@ public class SubscriptionTopicUtil { } return false; } + + /** + * Extracts source resource from bundle contained in {@link ResourceModifiedJsonMessage} payload. + * Used for R5 resource modified message handling. + */ + public static IBaseResource extractResourceFromBundle(FhirContext myFhirContext, IBaseBundle theBundle) { + List resources = BundleUtil.toListOfResources(myFhirContext, theBundle); + + return resources.stream() + .filter(SubscriptionStatus.class::isInstance) + .map(SubscriptionStatus.class::cast) + .flatMap(subscriptionStatus -> subscriptionStatus.getNotificationEvent().stream()) + .filter(SubscriptionStatus.SubscriptionStatusNotificationEventComponent::hasFocus) + .map(SubscriptionStatus.SubscriptionStatusNotificationEventComponent::getFocus) + .map(BaseReference::getResource) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java index e96f3470b50..adf5b851055 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java @@ -1,16 +1,28 @@ package ca.uhn.fhir.jpa.topic; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.Patient; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.SubscriptionStatus; import org.hl7.fhir.r5.model.SubscriptionTopic; import org.junit.jupiter.api.Test; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class SubscriptionTopicUtilTest { + + private final FhirContext myContext = FhirContext.forR5Cached(); + @Test public void testMatch() { // I know this is gross. I haven't found a nicer way to do this @@ -28,4 +40,50 @@ class SubscriptionTopicUtilTest { assertFalse(SubscriptionTopicUtil.matches(BaseResourceMessage.OperationTypeEnum.TRANSACTION, supportedTypes)); } + @Test + public void testExtractResourceFromBundle_withCorrectBundle_returnsCorrectResource() { + Patient patient = new Patient(); + patient.setId("Patient/1"); + Bundle bundle = buildSubscriptionStatus(patient); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertEquals(patient, extractionResult); + } + + @Test + public void testExtractResourceFromBundle_withoutReferenceResource_returnsNull() { + Bundle bundle = buildSubscriptionStatus(null); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertNull(extractionResult); + } + + private Bundle buildSubscriptionStatus(Resource theResource) { + SubscriptionStatus subscriptionStatus = new SubscriptionStatus(); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent event = + subscriptionStatus.addNotificationEvent(); + Reference reference = new Reference(); + reference.setResource(theResource); + event.setFocus(reference); + + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(subscriptionStatus); + bundle.addEntry().setResource(theResource); + return bundle; + } + + @Test + public void testExtractResourceFromBundle_withoutNotificationEvent_returnsNull() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new SubscriptionStatus()); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertNull(extractionResult); + } + + @Test + public void testExtractResourceFromBundle_withEmptyBundle_returnsNull() { + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, new Bundle()); + assertNull(extractionResult); + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java index 0207577abd7..448c32856ee 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java @@ -45,6 +45,7 @@ public class MdmConstants { "http://hl7.org/fhir/StructureDefinition/match-grade"; public static final String SYSTEM_GOLDEN_RECORD_STATUS = "http://hapifhir.io/fhir/NamingSystem/mdm-record-status"; + public static final String SUBSCRIPTION_TOPIC_URL = "http://hapifhir.io/fhir/r5/SubscriptionTopic/mdm"; public static final String CODE_GOLDEN_RECORD = "GOLDEN_RECORD"; public static final String CODE_GOLDEN_RECORD_REDIRECTED = "REDIRECTED"; public static final String DISPLAY_GOLDEN_RECORD = "Golden Record"; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java index 07f7858bc3a..7072483ec05 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java @@ -82,6 +82,13 @@ public class CanonicalEID { .setValue(myValue); } + public org.hl7.fhir.r5.model.Identifier toR5() { + return new org.hl7.fhir.r5.model.Identifier() + .setUse(org.hl7.fhir.r5.model.Identifier.IdentifierUse.fromCode(myUse)) + .setSystem(mySystem) + .setValue(myValue); + } + public String getSystem() { return mySystem; } 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 2bcdf08b4ee..e5f03242298 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 @@ -66,6 +66,7 @@ public class MdmProviderLoader { switch (myFhirContext.getVersion().getVersion()) { case DSTU3: case R4: + case R5: myResourceProviderFactory.addSupplier(() -> new MdmProviderDstu3Plus( myFhirContext, myMdmControllerSvc, diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java index 966a0e2ebd2..27e9b0603f9 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java @@ -49,6 +49,10 @@ public class DateTimeWrapper { org.hl7.fhir.r4.model.BaseDateTimeType r4Date = (org.hl7.fhir.r4.model.BaseDateTimeType) theDate; myPrecision = r4Date.getPrecision(); myValueAsString = r4Date.getValueAsString(); + } else if (theDate instanceof org.hl7.fhir.r5.model.BaseDateTimeType) { + org.hl7.fhir.r5.model.BaseDateTimeType r5Date = (org.hl7.fhir.r5.model.BaseDateTimeType) theDate; + myPrecision = r5Date.getPrecision(); + myValueAsString = r5Date.getValueAsString(); } else { // we should consider changing this error so we don't need the fhir context at all throw new UnsupportedOperationException(Msg.code(1520) + "Version not supported: " diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java index c7b46f4cd38..75be805220f 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java @@ -50,6 +50,7 @@ import javax.annotation.Nonnull; import static ca.uhn.fhir.context.FhirVersionEnum.DSTU3; import static ca.uhn.fhir.context.FhirVersionEnum.R4; +import static ca.uhn.fhir.context.FhirVersionEnum.R5; @Service public class GoldenResourceHelper { @@ -200,7 +201,7 @@ public class GoldenResourceHelper { private void validateContextSupported() { FhirVersionEnum fhirVersion = myFhirContext.getVersion().getVersion(); - if (fhirVersion == R4 || fhirVersion == DSTU3) { + if (fhirVersion == R4 || fhirVersion == DSTU3 || fhirVersion == R5) { return; } throw new UnsupportedOperationException(Msg.code(1489) + "Version not supported: " diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java index 403ddd2c4ff..7a0f728647d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java @@ -56,10 +56,12 @@ public final class IdentifierUtil { * @param theFhirContext FHIR context to use for determining the identifier version * @param eid EID to get equivalent FHIR Identifier from * @param Generic Identifier base interface - * @return Returns appropriate R4 or DSTU3 Identifier instance + * @return Returns appropriate R5, R4 or DSTU3 Identifier instance */ public static T toId(FhirContext theFhirContext, CanonicalEID eid) { switch (theFhirContext.getVersion().getVersion()) { + case R5: + return (T) eid.toR5(); case R4: return (T) eid.toR4(); case DSTU3: From 9f827a75c7760a6c6f65c5f9efb5e4d3b77cc6b1 Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Fri, 24 Nov 2023 09:49:33 -0500 Subject: [PATCH 42/86] Resolve 5480 Update member-match signature to match the spec (#5481) * - Changed member-match function param names, and return value - modified existing tests to accomodate for the change * removed tests unsuitable for new return value * resolve code review comments * version bump --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../java/ca/uhn/fhir/rest/api/Constants.java | 4 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- ...ber-match-signature-to-match-the-spec.yaml | 4 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../r4/MemberMatchR4ResourceProvider.java | 20 +-- .../provider/r4/MemberMatcherR4Helper.java | 13 +- .../r4/MemberMatcherR4HelperTest.java | 39 +---- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- .../r4/PatientMemberMatchOperationR4Test.java | 140 ++++++------------ hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 83 files changed, 148 insertions(+), 230 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 6f8821871d0..14ae49d44f6 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index a709106324f..c8d934ef99a 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 49356c507ab..595f263a0df 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index e6301929bfa..adce742a4f9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -227,8 +227,8 @@ public class Constants { public static final String PARAM_MEMBER_IDENTIFIER = "MemberIdentifier"; - public static final String PARAM_OLD_COVERAGE = "OldCoverage"; - public static final String PARAM_NEW_COVERAGE = "NewCoverage"; + public static final String COVERAGE_TO_MATCH = "CoverageToMatch"; + public static final String COVERAGE_TO_LINK = "CoverageToLink"; public static final String PARAM_CONSENT = "Consent"; public static final String PARAM_MEMBER_PATIENT_NAME = PARAM_MEMBER_PATIENT + " Name"; public static final String PARAM_MEMBER_PATIENT_BIRTHDATE = PARAM_MEMBER_PATIENT + " Birthdate"; diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 81e7e9aec1f..d2c92c7102f 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index b9317cac55f..a09540f3cb5 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-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 3cdf6295cf1..6cdef901a35 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index a1446d3b7e9..0dfb56390d6 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index a7602e782d2..d16c69b118e 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 08cf10fe4e4..5b7c57a6612 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index f3841700d54..a0acac6d5bc 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 62bfffc3449..199daabb89d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 9f5e2d97868..ed159f4d302 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 04a44e817a0..c8fb88de598 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml new file mode 100644 index 00000000000..7f19110cde1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5480 +title: "Updated $member-match operation signature to match the latest specification." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 8b14ff12758..29520193ebd 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index ac6893ad3c7..e217e6b86b9 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index a525ae31628..79d5c793ed2 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index f9c3baca44f..1ebd7add4ce 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java index 5234782144a..4d09f9fed84 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java @@ -59,18 +59,18 @@ public class MemberMatchR4ResourceProvider { returnParameters = {@OperationParam(name = "MemberIdentifier", typeName = "string")}) public Parameters patientMemberMatch( javax.servlet.http.HttpServletRequest theServletRequest, - @Description( - shortDefinition = - "The target of the operation. Will be returned with Identifier for matched coverage added.") + @Description(shortDefinition = "The target of the operation. Contain member Patient demographics.") @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) Patient theMemberPatient, - @Description(shortDefinition = "Old coverage information as extracted from beneficiary's card.") - @OperationParam(name = Constants.PARAM_OLD_COVERAGE, min = 1, max = 1) + @Description( + shortDefinition = + "Old coverage information as extracted from beneficiary's card. Identifies the coverage to be matched by the receiving payer.") + @OperationParam(name = Constants.COVERAGE_TO_MATCH, min = 1, max = 1) Coverage oldCoverage, @Description( shortDefinition = - "New Coverage information. Provided as a reference. Optionally returned unmodified.") - @OperationParam(name = Constants.PARAM_NEW_COVERAGE, min = 1, max = 1) + "New Coverage information. Identifies the coverage information of the member as they are known by the requesting payer. Provided as a reference.") + @OperationParam(name = Constants.COVERAGE_TO_LINK, min = 1, max = 1) Coverage newCoverage, @Description( shortDefinition = @@ -129,14 +129,14 @@ public class MemberMatchR4ResourceProvider { myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); myMemberMatcherR4Helper.updateConsentForMemberMatch(theConsent, patient, theMemberPatient, theRequestDetails); - return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink, theConsent); + return myMemberMatcherR4Helper.buildSuccessReturnParameters(patient); } private void validateParams( Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { validateParam(theMemberPatient, Constants.PARAM_MEMBER_PATIENT); - validateParam(theOldCoverage, Constants.PARAM_OLD_COVERAGE); - validateParam(theNewCoverage, Constants.PARAM_NEW_COVERAGE); + validateParam(theOldCoverage, Constants.COVERAGE_TO_MATCH); + validateParam(theNewCoverage, Constants.COVERAGE_TO_LINK); validateParam(theConsent, Constants.PARAM_CONSENT); validateMemberPatientParam(theMemberPatient); validateConsentParam(theConsent); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java index d9a271feac9..d0d564687eb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java @@ -51,10 +51,7 @@ import java.util.Optional; import java.util.function.Consumer; import javax.annotation.Nullable; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; public class MemberMatcherR4Helper { static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MemberMatcherR4Helper.class); @@ -144,13 +141,13 @@ public class MemberMatcherR4Helper { myConsentDao.create(theConsent, theRequestDetails); } - public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage, Consent theConsent) { + public Parameters buildSuccessReturnParameters(Patient thePatient) { IBaseParameters parameters = ParametersUtil.newInstance(myFhirContext); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_MEMBER_PATIENT, theMemberPatient); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_NEW_COVERAGE, theCoverage); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_CONSENT, theConsent); ParametersUtil.addParameterToParameters( - myFhirContext, parameters, PARAM_MEMBER_IDENTIFIER, getIdentifier(theMemberPatient)); + myFhirContext, + parameters, + PARAM_MEMBER_IDENTIFIER, + thePatient.getIdElement().toUnqualifiedVersionless()); return (Parameters) parameters; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java index fa9384d5288..88323c8a50e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.IQueryParameterType; @@ -20,6 +19,7 @@ import org.hl7.fhir.r4.model.Consent; import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; @@ -40,13 +40,9 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -162,38 +158,13 @@ public class MemberMatcherR4HelperTest { identifierType.addCoding(new Coding("", "MB", "")); identifier.setType(identifierType); Patient patient = new Patient(); - Coverage coverage = new Coverage(); - Consent consent = new Consent(); + patient.setId("Patient/test123"); patient.addIdentifier(identifier); - Parameters result = myHelper.buildSuccessReturnParameters(patient, coverage, consent); + Parameters result = myHelper.buildSuccessReturnParameters(patient); - assertEquals(PARAM_MEMBER_PATIENT, result.getParameter().get(0).getName()); - assertEquals(patient, result.getParameter().get(0).getResource()); - - assertEquals(PARAM_NEW_COVERAGE, result.getParameter().get(1).getName()); - assertEquals(coverage, result.getParameter().get(1).getResource()); - - assertEquals(PARAM_CONSENT, result.getParameter().get(2).getName()); - assertEquals(consent, result.getParameter().get(2).getResource()); - - assertEquals(PARAM_MEMBER_IDENTIFIER, result.getParameter().get(3).getName()); - assertEquals(identifier, result.getParameter().get(3).getValue()); - } - - @Test - void buildNotSuccessReturnParameters_IncorrectPatientIdentifier() { - Identifier identifier = new Identifier(); - Patient patient = new Patient(); - Coverage coverage = new Coverage(); - Consent consent = new Consent(); - patient.addIdentifier(identifier); - - try { - myHelper.buildSuccessReturnParameters(patient, coverage, consent); - } catch (Exception e) { - assertThat(e.getMessage(), startsWith(Msg.code(2219))); - } + assertEquals(PARAM_MEMBER_IDENTIFIER, result.getParameter().get(0).getName()); + assertEquals(patient.getId(), ((IdType)(result.getParameter().get(0).getValue())).getValue()); } @Test diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 3d76846bda2..7e0dfd800ab 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 43c285f9e4e..bac3221b4e4 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 97ec14f2cc6..747bea9ba73 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index dd626c0728c..699b9e6e1a8 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index ee576bed519..dbcafa398b4 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index fe4185c5e32..9e204079c6d 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index ee8d7e75b73..c88d574ed07 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 - 6.11.1-SNAPSHOT + 6.11.2-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 1b49c97a911..b0408c3f65e 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index cc466d934e8..5ac8bc1e406 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 163ec756332..7712a6347ba 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java index 638e9a5f0db..fc2cad47867 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java @@ -12,17 +12,7 @@ import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.*; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -32,26 +22,21 @@ import org.springframework.beans.factory.annotation.Autowired; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Optional; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PATIENT_REFERENCE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PERFORMER_REFERENCE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_BIRTHDATE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_NAME; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_OLD_COVERAGE; +import static ca.uhn.fhir.rest.api.Constants.*; +import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; @SuppressWarnings("Duplicates") public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientMemberMatchOperationR4Test.class); private static final String ourQuery = "/Patient/$member-match?_format=json"; + private static final String PATIENT_ID = "Patient/A123"; private static final String EXISTING_COVERAGE_ID = "cov-id-123"; private static final String EXISTING_COVERAGE_IDENT_SYSTEM = "http://centene.com/insurancePlanIds"; private static final String EXISTING_COVERAGE_IDENT_VALUE = "U1234567890"; @@ -62,8 +47,8 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes private Identifier ourExistingCoverageIdentifier; private Patient myPatient; - private Coverage oldCoverage; // Old Coverage (must match field) - private Coverage newCoverage; // New Coverage (must return unchanged) + private Coverage coverageToMatch; // Old Coverage (must match field) + private Coverage coverageToLink; private Consent myConsent; @Autowired @@ -96,14 +81,14 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes myPatient = (Patient) new Patient().setName(Lists.newArrayList(new HumanName() .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) .setBirthDateElement(new DateType("2020-01-01")) - .setId("Patient/A123"); + .setId(PATIENT_ID); // Old Coverage (must match field) - oldCoverage = (Coverage) new Coverage() + coverageToMatch = (Coverage) new Coverage() .setId(EXISTING_COVERAGE_ID); // New Coverage (must return unchanged) - newCoverage = (Coverage) new Coverage() + coverageToLink = (Coverage) new Coverage() .setIdentifier(Lists.newArrayList(new Identifier().setSystem("http://newealthplan.example.com").setValue("234567"))) .setId("AA87654"); @@ -111,8 +96,8 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes .setStatus(Consent.ConsentState.ACTIVE) .setScope(new CodeableConcept().addCoding(new Coding("http://terminology.hl7.org/CodeSystem/consentscope", "patient-privacy", null))) .addPolicy(new Consent.ConsentPolicyComponent().setUri(CONSENT_POLICY_SENSITIVE_DATA_URI)) - .setPatient(new Reference("Patient/A123")) - .addPerformer(new Reference("Patient/A123")); + .setPatient(new Reference(PATIENT_ID)) + .addPerformer(new Reference(PATIENT_ID)); myServer.getRestfulServer().registerProvider(theMemberMatchR4ResourceProvider); myMemberMatcherR4Helper.setRegularFilterSupported(false); } @@ -126,7 +111,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes member.setName(Lists.newArrayList(new HumanName() .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) .setBirthDateElement(new DateType("2020-01-01")) - .setId("Patient/A123"); + .setId(PATIENT_ID); if (includeBeneficiaryIdentifier) { member.setIdentifier(Collections.singletonList(new Identifier() .setSystem(EXISTING_COVERAGE_PATIENT_IDENT_SYSTEM).setValue(EXISTING_COVERAGE_PATIENT_IDENT_VALUE))); @@ -150,24 +135,11 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes myClient.create().resource(ourExistingCoverage).execute().getId().toUnqualifiedVersionless().getValue(); } - @Test - public void testMemberMatchByCoverageId() throws Exception { - createCoverageWithBeneficiary(true, true); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - @Test public void testCoverageNoBeneficiaryReturns422() throws Exception { createCoverageWithBeneficiary(false, false); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find beneficiary for coverage."); } @@ -176,7 +148,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testCoverageBeneficiaryNoIdentifierReturns422() throws Exception { createCoverageWithBeneficiary(true, false); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Coverage beneficiary does not have an identifier."); } @@ -186,7 +158,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes createCoverageWithBeneficiary(true, true); myPatient.setName(Lists.newArrayList(new HumanName().setUse(HumanName.NameUse.OFFICIAL).setFamily("Smith"))); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find matching patient for coverage."); } @@ -196,7 +168,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes createCoverageWithBeneficiary(true, false); myPatient.setBirthDateElement(new DateType("2000-01-01")); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Could not find matching patient for coverage."); } @@ -205,7 +177,7 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testRegularContentProfileAccessWithRegularNotAllowedReturns422() throws Exception { createCoverageWithBeneficiary(true, true); myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, "Consent policy does not match the data release segmentation capabilities."); } @@ -213,12 +185,12 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes @Test public void testSensitiveContentProfileAccessWithRegularNotAllowed() throws Exception { createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); Parameters parametersResponse = performOperation(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters); - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); + validateMemberIdentifier(parametersResponse); } @Test @@ -226,44 +198,18 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes createCoverageWithBeneficiary(true, true); myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); myMemberMatcherR4Helper.setRegularFilterSupported(true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); Parameters parametersResponse = performOperation(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters); - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - @Test - public void testConsentReturns() throws Exception { - createCoverageWithBeneficiary(true, true); - myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); - myMemberMatcherR4Helper.setRegularFilterSupported(true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - validateResponseConsent(parametersResponse, myConsent); - } - - @Test - public void testMemberMatchByCoverageIdentifier() throws Exception { - createCoverageWithBeneficiary(true, true); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); + validateMemberIdentifier(parametersResponse); } /** * Validates that second resource from the response is same as the received coverage */ private void validateNewCoverage(Parameters theResponse, Coverage theOriginalCoverage) { - List patientList = ParametersUtil.getNamedParameters(this.getFhirContext(), theResponse, PARAM_NEW_COVERAGE); + List patientList = ParametersUtil.getNamedParameters(this.getFhirContext(), theResponse, COVERAGE_TO_LINK); assertEquals(1, patientList.size()); Coverage respCoverage = (Coverage) theResponse.getParameter().get(1).getResource(); @@ -309,29 +255,29 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes assertEquals(EXISTING_COVERAGE_PATIENT_IDENT_VALUE, addedIdentifier.getValue()); } - @Test - public void testNoCoverageMatchFound() throws Exception { - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Could not find coverage for member"); + /** + * validates that the returned patient ID is correct + */ + private void validateMemberIdentifier(Parameters theResponse){ + assertTrue(theResponse.hasParameter(PARAM_MEMBER_IDENTIFIER)); + Optional memberIdentifierOptional = ParametersUtil.getNamedParameter(this.getFhirContext(), theResponse, PARAM_MEMBER_IDENTIFIER); + assertTrue(memberIdentifierOptional.isPresent()); + IdType memberIdentifier = (IdType) ((Parameters.ParametersParameterComponent) memberIdentifierOptional.get()).getValue(); + assertEquals(PATIENT_ID, memberIdentifier.getValue()); } @Test - public void testConsentUpdatePatientAndPerformer() throws Exception { - createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - Consent respConsent = validateResponseConsent(parametersResponse, myConsent); - validateConsentPatientAndPerformerRef(myPatient, respConsent); + public void testNoCoverageMatchFound() throws Exception { + Parameters inputParameters = buildInputParameters(myPatient, coverageToMatch, coverageToLink, myConsent); + performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, + "Could not find coverage for member"); } private Parameters buildInputParameters(Patient thePatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { Parameters p = new Parameters(); ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_MEMBER_PATIENT, thePatient); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_OLD_COVERAGE, theOldCoverage); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_NEW_COVERAGE, theNewCoverage); + ParametersUtil.addParameterToParameters(this.getFhirContext(), p, COVERAGE_TO_MATCH, theOldCoverage); + ParametersUtil.addParameterToParameters(this.getFhirContext(), p, COVERAGE_TO_LINK, theNewCoverage); ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_CONSENT, theConsent); return p; } @@ -434,14 +380,14 @@ public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Tes public void testInvalidOldCoverage() throws Exception { Parameters inputParameters = buildInputParameters(ourPatient, new Coverage(), ourNewCoverage, ourConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_OLD_COVERAGE + "\\\" is required."); + "Parameter \\\"" + COVERAGE_TO_MATCH + "\\\" is required."); } @Test public void testInvalidNewCoverage() throws Exception { Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, new Coverage(), ourConsent); performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_NEW_COVERAGE + "\\\" is required."); + "Parameter \\\"" + COVERAGE_TO_LINK + "\\\" is required."); } @Test diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index dda2f61c2fa..41bcc967b15 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index b5c3dbed217..4e0b6f578c5 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 336c98f999e..f9cb2801cba 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 6e5a97eca04..0bc7f2bb52a 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 171e049604d..13964da20d3 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index af536a1acbb..43be75c5975 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 286e6387ab0..4b257238089 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index e13e1a075df..87545c43398 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index df6b241f45f..d9bd49738d6 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 - 6.11.1-SNAPSHOT + 6.11.2-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 6414256d3aa..088209cc351 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 085610ef2be..687afbf9a17 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 - 6.11.1-SNAPSHOT + 6.11.2-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 39e70955007..87ea52486a0 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index a2cf0594f98..13dd141193d 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.11.1-SNAPSHOT + 6.11.2-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 616ab7f9836..e6c86b9552e 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 - 6.11.1-SNAPSHOT + 6.11.2-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 aa4731815a4..f19a6bda58f 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 - 6.11.1-SNAPSHOT + 6.11.2-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 6ee088ed768..f88d87a0b98 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 - 6.11.1-SNAPSHOT + 6.11.2-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 280627421c8..2e09e1b0da9 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 - 6.11.1-SNAPSHOT + 6.11.2-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 f8d1e7f6796..a8ea0668e3d 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 - 6.11.1-SNAPSHOT + 6.11.2-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 59dabbf94fd..6da838865ba 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 87b749a9885..5308ca8d290 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index efb9e42a2f2..ad912b14e25 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index a0ddd051b40..665b79c1640 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 23bc7cff5a9..5c28ea9bcd4 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index de8dcda41e1..e502c1a2cd8 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index b5f9ecce3c5..592593f7fb6 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index ea46e88b227..e6437a1f45d 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index be20e2d7ed8..fb29ef17446 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 6770994f0d3..e6c24290983 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-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 69722db4be2..3db931c5d24 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index a2fdb377fe7..b606a900cd7 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index ef95578aab5..6b64efeb95c 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 - 6.11.1-SNAPSHOT + 6.11.2-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 2111dfa959e..f0e3e2214b8 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 67e252f4387..95bf17a3aa3 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 819e3e82a34..14a7cbc6011 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 03ab2cdb626..80ea9d8936b 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 10ce616b5e7..41e519eb390 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 2ddd1a85eab..e12e18330c8 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index f0bffc2308c..97ea5b5fb54 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 - 6.11.1-SNAPSHOT + 6.11.2-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 e03891aa0c6..83b7ddb4706 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 - 6.11.1-SNAPSHOT + 6.11.2-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 fb09d678679..c4c98ed5d6f 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 - 6.11.1-SNAPSHOT + 6.11.2-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 fe683d25136..63bf9d4eae2 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 - 6.11.1-SNAPSHOT + 6.11.2-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 1eb14c3ae13..3d544b03e9d 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 - 6.11.1-SNAPSHOT + 6.11.2-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 de048b747a5..ec5a833c234 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index f53d993ec4f..80b4cc6a3a1 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index bf0e98e04f9..1b06c741ab1 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 2a483fdd801..339591a3322 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index fc49e3185d9..cc15c9fe356 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.11.1-SNAPSHOT + 6.11.2-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 3e83bda6a90..1af297a0476 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 - 6.11.1-SNAPSHOT + 6.11.2-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 ddcd9410f57..9996e300079 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 - 6.11.1-SNAPSHOT + 6.11.2-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 2aca947a898..249b5358d82 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 - 6.11.1-SNAPSHOT + 6.11.2-SNAPSHOT ../../pom.xml From d546ce34e5cc3ed7348c9ea12d62696167db2825 Mon Sep 17 00:00:00 2001 From: TynerGjs <132295567+TynerGjs@users.noreply.github.com> Date: Fri, 24 Nov 2023 13:40:13 -0500 Subject: [PATCH 43/86] Resolve 5483-update-consent-storage-to-latest-spec (#5484) * removed the adding of memberIdentifier to the consent * - fixed corresponding test - added changelog --- .../7_0_0/5483-update-consent-storage-to-latest-spec.yaml | 4 ++++ .../uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java | 8 -------- .../fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java | 7 +------ 3 files changed, 5 insertions(+), 14 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml new file mode 100644 index 00000000000..18a71501d4b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5480 +title: "Updated Consent storage in $member-match operation." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java index d0d564687eb..b05cec467fe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java @@ -133,7 +133,6 @@ public class MemberMatcherR4Helper { public void updateConsentForMemberMatch( Consent theConsent, Patient thePatient, Patient theMemberPatient, RequestDetails theRequestDetails) { - addIdentifierToConsent(theConsent, theMemberPatient); updateConsentPatientAndPerformer(theConsent, thePatient); myConsentModifier.accept(theConsent); @@ -274,13 +273,6 @@ public class MemberMatcherR4Helper { return policyTypes.equals(CONSENT_POLICY_REGULAR_TYPE) && myRegularFilterSupported; } - private void addIdentifierToConsent(Consent theConsent, Patient thePatient) { - String consentId = getIdentifier(thePatient).getValue(); - Identifier consentIdentifier = - new Identifier().setSystem(CONSENT_IDENTIFIER_CODE_SYSTEM).setValue(consentId); - theConsent.addIdentifier(consentIdentifier); - } - public void setRegularFilterSupported(boolean theRegularFilterSupported) { myRegularFilterSupported = theRegularFilterSupported; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java index 88323c8a50e..9f46be520dc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java @@ -479,7 +479,7 @@ public class MemberMatcherR4HelperTest { private ArgumentCaptor myDaoCaptor; @Test - public void updateConsentForMemberMatch_noProvider_addsIdentifierUpdatePatientButNotExtensionAndSaves() { + public void updateConsentForMemberMatch_noProvider_UpdatePatientButNotExtensionAndSaves() { // setup Consent consent = getConsent(); consent.addPolicy(constructConsentPolicyComponent("#sensitive")); @@ -493,11 +493,6 @@ public class MemberMatcherR4HelperTest { verify(myConsentDao).create(myDaoCaptor.capture(), same(myRequestDetails)); Consent saved = myDaoCaptor.getValue(); - // check consent identifier - assertEquals(1, saved.getIdentifier().size()); - assertEquals(MemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM, saved.getIdentifier().get(0).getSystem()); - assertNotNull(saved.getIdentifier().get(0).getValue()); - assertEquals(saved.getIdentifier().get(0).getValue(), memberPatient.getIdentifier().get(0).getValue()); // check consent patient info String patientRef = patient.getIdElement().toUnqualifiedVersionless().getValue(); From f39dbb57519d2bf7eb9bcb6d6e8813aca0be7dcc Mon Sep 17 00:00:00 2001 From: dotasek Date: Fri, 24 Nov 2023 14:06:18 -0500 Subject: [PATCH 44/86] Bump setup-java action to v3 (#5491) --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7874bf40a3b..44fed4bfde0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: if: ${{ github.event_name == 'pull_request' }} - name: Setup java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: zulu java-version: 17 From 4cc4682245989649a7a9e53b6b2d24a13e2ddb38 Mon Sep 17 00:00:00 2001 From: Etienne Poirier <33007955+epeartree@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:55:03 -0500 Subject: [PATCH 45/86] CLI tool command migrate-database executing in dry-run mode insert entries into table FLY_HFJ_MIGRATION (#5487) * initial test * Solution with changelog. * making spotless hapi * addressing comments from code reviews * making the test better. * addressing code review comment and adding test. --------- Co-authored-by: peartree --- .../HapiFlywayMigrateDatabaseCommandTest.java | 52 +++++++++++++++++++ ...grate-database-in-dry-run-modifies-db.yaml | 6 +++ .../ca/uhn/fhir/jpa/migrate/HapiMigrator.java | 8 ++- 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java index a24150e9079..cebbf9c9a8b 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java @@ -2,6 +2,9 @@ package ca.uhn.fhir.cli; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; +import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; +import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.base.Charsets; import org.apache.commons.io.FileUtils; @@ -123,11 +126,13 @@ public class HapiFlywayMigrateDatabaseCommandTest { String url = "jdbc:h2:" + location.getAbsolutePath(); DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "", ""); + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(connectionProperties.getDataSource(), connectionProperties.getDriverType(), SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME); String initSql = "/persistence_create_h2_340.sql"; executeSqlStatements(connectionProperties, initSql); seedDatabase340(connectionProperties); + seedDatabaseMigration340(hapiMigrationDao); ourLog.info("**********************************************"); ourLog.info("Done Setup, Starting Migration..."); @@ -160,6 +165,7 @@ public class HapiFlywayMigrateDatabaseCommandTest { // Verify that foreign key FK_SEARCHRES_RES on HFJ_SEARCH_RESULT exists foreignKeys = JdbcUtils.getForeignKeys(connectionProperties, "HFJ_RESOURCE", "HFJ_SEARCH_RESULT"); assertTrue(foreignKeys.contains("FK_SEARCHRES_RES")); + int expectedMigrationEntities = hapiMigrationDao.findAll().size(); App.main(args); @@ -181,6 +187,8 @@ public class HapiFlywayMigrateDatabaseCommandTest { // Verify that foreign key FK_SEARCHRES_RES on HFJ_SEARCH_RESULT still exists foreignKeys = JdbcUtils.getForeignKeys(connectionProperties, "HFJ_RESOURCE", "HFJ_SEARCH_RESULT"); assertTrue(foreignKeys.contains("FK_SEARCHRES_RES")); + assertTrue(expectedMigrationEntities == hapiMigrationDao.findAll().size()); + } @Test @@ -210,6 +218,38 @@ public class HapiFlywayMigrateDatabaseCommandTest { assertTrue(JdbcUtils.getTableNames(connectionProperties).contains("HFJ_BLK_EXPORT_JOB")); // Late table } + @Test + public void testMigrateFrom340_dryRun_whenNoMigrationTableExists() throws IOException, SQLException { + + File location = getLocation("migrator_h2_test_340_dryrun"); + + String url = "jdbc:h2:" + location.getAbsolutePath(); + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "", ""); + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(connectionProperties.getDataSource(), connectionProperties.getDriverType(), SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME); + + String initSql = "/persistence_create_h2_340.sql"; + executeSqlStatements(connectionProperties, initSql); + + seedDatabase340(connectionProperties); + + ourLog.info("**********************************************"); + ourLog.info("Done Setup, Starting Migration..."); + ourLog.info("**********************************************"); + + String[] args = new String[]{ + BaseFlywayMigrateDatabaseCommand.MIGRATE_DATABASE, + "-d", "H2_EMBEDDED", + "-u", url, + "-n", "", + "-p", "", + "-r" + }; + + App.main(args); + + assertFalse(JdbcUtils.getTableNames(connectionProperties).contains("FLY_HFJ_MIGRATION")); + } + @Nonnull private File getLocation(String theDatabaseName) throws IOException { File directory = new File(DB_DIRECTORY); @@ -360,4 +400,16 @@ public class HapiFlywayMigrateDatabaseCommandTest { } + private void seedDatabaseMigration340(HapiMigrationDao theHapiMigrationDao) { + theHapiMigrationDao.createMigrationTableIfRequired(); + HapiMigrationEntity hapiMigrationEntity = new HapiMigrationEntity(); + hapiMigrationEntity.setPid(1); + hapiMigrationEntity.setVersion("3.4.0.20180401.1"); + hapiMigrationEntity.setDescription("some sql statement"); + hapiMigrationEntity.setExecutionTime(25); + hapiMigrationEntity.setSuccess(true); + + theHapiMigrationDao.save(hapiMigrationEntity); + } + } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml new file mode 100644 index 00000000000..f46b55475bd --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5486 +jira: SMILE-7457 +title: "Previously, testing database migration with cli migrate-database command in dry-run mode would insert in the +migration task table. The issue has been fixed." diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java index 302b05cde23..6b0fecdd89c 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java @@ -188,7 +188,9 @@ public class HapiMigrator { } private void postExecute(BaseTask theNext, StopWatch theStopWatch, boolean theSuccess) { - myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theStopWatch.getMillis()), theSuccess); + if (!theNext.isDryRun()) { + myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theStopWatch.getMillis()), theSuccess); + } } public void addTasks(Iterable theMigrationTasks) { @@ -219,6 +221,8 @@ public class HapiMigrator { } public void createMigrationTableIfRequired() { - myHapiMigrationStorageSvc.createMigrationTableIfRequired(); + if (!myDryRun) { + myHapiMigrationStorageSvc.createMigrationTableIfRequired(); + } } } From 83bfa817aebc7f8e176eb975fe31c3e1d9e169f7 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Mon, 27 Nov 2023 17:32:58 -0500 Subject: [PATCH 46/86] Batch - ensure we target the default partition for non-gated jobs (#5496) * Target the default partition for batch2 storage. --- .../5496-batch-default-partition-non-gated.yaml | 4 ++++ .../fhir/jpa/batch2/JpaJobPersistenceImpl.java | 1 + .../fhir/batch2/config/BaseBatch2Config.java | 9 ++++++--- .../batch2/coordinator/JobCoordinatorImpl.java | 2 ++ .../fhir/batch2/coordinator/JobDataSink.java | 13 +++++++++++-- .../batch2/coordinator/WorkChunkProcessor.java | 17 ++++++++++++++--- .../coordinator/JobCoordinatorImplTest.java | 2 +- .../batch2/coordinator/JobDataSinkTest.java | 7 ++++++- .../coordinator/WorkChunkProcessorTest.java | 3 ++- 9 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml new file mode 100644 index 00000000000..0b8253d22a0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5496 +title: "Ensure batch jobs target the default partition for non-gated steps." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java index a90a88cb4f9..12bf7d12cec 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java @@ -100,6 +100,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { } @Override + @Transactional(propagation = Propagation.REQUIRED) public String onWorkChunkCreate(WorkChunkCreateEvent theBatchWorkChunk) { Batch2WorkChunkEntity entity = new Batch2WorkChunkEntity(); entity.setId(UUID.randomUUID().toString()); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java index 071a89fe716..4395264fcd5 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java @@ -48,10 +48,13 @@ public abstract class BaseBatch2Config { public static final String CHANNEL_NAME = "batch2-work-notification"; @Autowired - private IJobPersistence myPersistence; + IJobPersistence myPersistence; @Autowired - private IChannelFactory myChannelFactory; + IChannelFactory myChannelFactory; + + @Autowired + IHapiTransactionService myHapiTransactionService; @Bean public JobDefinitionRegistry batch2JobDefinitionRegistry() { @@ -60,7 +63,7 @@ public abstract class BaseBatch2Config { @Bean public WorkChunkProcessor jobStepExecutorService(BatchJobSender theBatchJobSender) { - return new WorkChunkProcessor(myPersistence, theBatchJobSender); + return new WorkChunkProcessor(myPersistence, theBatchJobSender, myHapiTransactionService); } @Bean diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java index b51732df3be..a8935f5046a 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java @@ -43,6 +43,7 @@ import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.springframework.data.domain.Page; import org.springframework.messaging.MessageHandler; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -144,6 +145,7 @@ public class JobCoordinatorImpl implements IJobCoordinator { IJobPersistence.CreateResult instanceAndFirstChunk = myTransactionService .withSystemRequestOnDefaultPartition() + .withPropagation(Propagation.REQUIRES_NEW) .execute(() -> myJobPersistence.onCreateWithFirstChunk(jobDefinition, theStartRequest.getParameters())); JobWorkNotification workNotification = JobWorkNotification.firstStepNotification( diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java index 26fc1c922c7..5881e349779 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java @@ -28,10 +28,12 @@ import ca.uhn.fhir.batch2.model.JobWorkNotification; import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; import ca.uhn.fhir.batch2.model.WorkChunkData; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.Logs; import org.slf4j.Logger; +import org.springframework.transaction.annotation.Propagation; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -49,13 +51,15 @@ class JobDataSink myLastChunkId = new AtomicReference<>(); private final boolean myGatedExecution; + private final IHapiTransactionService myHapiTransactionService; JobDataSink( @Nonnull BatchJobSender theBatchJobSender, @Nonnull IJobPersistence theJobPersistence, @Nonnull JobDefinition theDefinition, @Nonnull String theInstanceId, - @Nonnull JobWorkCursor theJobWorkCursor) { + @Nonnull JobWorkCursor theJobWorkCursor, + IHapiTransactionService theHapiTransactionService) { super(theInstanceId, theJobWorkCursor); myBatchJobSender = theBatchJobSender; myJobPersistence = theJobPersistence; @@ -63,6 +67,7 @@ class JobDataSink myJobPersistence.onWorkChunkCreate(batchWorkChunk)); + myLastChunkId.set(chunkId); if (!myGatedExecution) { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java index 0c5dd4ba7c9..a68d7dbbcc9 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java @@ -29,6 +29,7 @@ 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.WorkChunk; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; import org.apache.commons.lang3.Validate; @@ -55,11 +56,16 @@ public class WorkChunkProcessor { private final IJobPersistence myJobPersistence; private final BatchJobSender myBatchJobSender; private final StepExecutor myStepExecutor; + private final IHapiTransactionService myHapiTransactionService; - public WorkChunkProcessor(IJobPersistence theJobPersistence, BatchJobSender theSender) { + public WorkChunkProcessor( + IJobPersistence theJobPersistence, + BatchJobSender theSender, + IHapiTransactionService theHapiTransactionService) { myJobPersistence = theJobPersistence; myBatchJobSender = theSender; myStepExecutor = new StepExecutor(theJobPersistence); + myHapiTransactionService = theHapiTransactionService; } /** @@ -118,8 +124,13 @@ public class WorkChunkProcessor { dataSink = (BaseDataSink) new FinalStepDataSink<>( theJobDefinition.getJobDefinitionId(), theInstanceId, theCursor.asFinalCursor()); } else { - dataSink = - new JobDataSink<>(myBatchJobSender, myJobPersistence, theJobDefinition, theInstanceId, theCursor); + dataSink = new JobDataSink<>( + myBatchJobSender, + myJobPersistence, + theJobDefinition, + theInstanceId, + theCursor, + myHapiTransactionService); } return dataSink; } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java index 12ef54af08c..983432c62b0 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java @@ -94,7 +94,7 @@ public class JobCoordinatorImplTest extends BaseBatch2Test { public void beforeEach() { // The code refactored to keep the same functionality, // but in this service (so it's a real service here!) - WorkChunkProcessor jobStepExecutorSvc = new WorkChunkProcessor(myJobInstancePersister, myBatchJobSender); + WorkChunkProcessor jobStepExecutorSvc = new WorkChunkProcessor(myJobInstancePersister, myBatchJobSender, new NonTransactionalHapiTransactionService()); mySvc = new JobCoordinatorImpl(myBatchJobSender, myWorkChannelReceiver, myJobInstancePersister, myJobDefinitionRegistry, jobStepExecutorSvc, myJobMaintenanceService, myTransactionService); } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java index 1fb4274bcbe..a7e064f9e68 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java @@ -14,6 +14,8 @@ import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobWorkCursor; import ca.uhn.fhir.batch2.model.JobWorkNotification; import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import com.fasterxml.jackson.annotation.JsonProperty; @@ -31,6 +33,7 @@ import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -54,6 +57,7 @@ class JobDataSinkTest { private ArgumentCaptor myJobWorkNotificationCaptor; @Captor private ArgumentCaptor myBatchWorkChunkCaptor; + private final IHapiTransactionService myHapiTransactionService = new NonTransactionalHapiTransactionService(); @Test public void test_sink_accept() { @@ -98,7 +102,7 @@ class JobDataSinkTest { JobInstance instance = JobInstance.fromInstanceId(JOB_INSTANCE_ID); StepExecutionDetails details = new StepExecutionDetails<>(new TestJobParameters().setParam1("" + PID_COUNT), null, instance, CHUNK_ID); JobWorkCursor cursor = new JobWorkCursor<>(job, true, firstStep, lastStep); - JobDataSink sink = new JobDataSink<>(myBatchJobSender, myJobPersistence, job, JOB_INSTANCE_ID, cursor); + JobDataSink sink = new JobDataSink<>(myBatchJobSender, myJobPersistence, job, JOB_INSTANCE_ID, cursor, myHapiTransactionService); RunOutcome result = firstStepWorker.run(details, sink); @@ -122,6 +126,7 @@ class JobDataSinkTest { assertEquals(JOB_DEF_ID, batchWorkChunk.jobDefinitionId); assertEquals(JOB_INSTANCE_ID, batchWorkChunk.instanceId); assertEquals(LAST_STEP_ID, batchWorkChunk.targetStepId); + assertNotNull(batchWorkChunk.serializedData); Step1Output stepOutput = JsonUtil.deserialize(batchWorkChunk.serializedData, Step1Output.class); assertThat(stepOutput.getPids(), hasSize(PID_COUNT)); } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java index 83cc133cb95..10f1a007e5c 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java @@ -21,6 +21,7 @@ import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent; import ca.uhn.fhir.batch2.model.WorkChunkData; import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; +import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import org.junit.jupiter.api.BeforeEach; @@ -428,7 +429,7 @@ public class WorkChunkProcessorTest { private class TestWorkChunkProcessor extends WorkChunkProcessor { public TestWorkChunkProcessor(IJobPersistence thePersistence, BatchJobSender theSender) { - super(thePersistence, theSender); + super(thePersistence, theSender, new NonTransactionalHapiTransactionService()); } @Override From 63eed3936ba25464412509e976e19290760ab991 Mon Sep 17 00:00:00 2001 From: Taha Date: Mon, 27 Nov 2023 20:12:35 -0800 Subject: [PATCH 47/86] (#5442) fetch should also resolve canonical URL references (#5443) * (#5442) fetch should also resolve canonical URL references * (#5442) - added test * (#5442) - cleanup? * (#5442) - changelog and reorganize test * (#5442) PR feedback * (#5442) PR feedback * (#5442) cleared ValidatorResourceFetcher linter warnings * (#5442) - new error code * (#5442) caught additional error * (#5442) spotless apply * (#5442) spotless apply --------- Co-authored-by: taha.attari@smilecdr.com --- ...validation-fetcher-fetch-by-canonical.yaml | 4 + .../fhir/jpa/api/dao/IFhirResourceDao.java | 21 +- .../validation/ValidatorResourceFetcher.java | 60 +- .../ValidatorResourceFetcherTest.java | 65 + .../resources/q_jon_with_url_version.json | 1388 +++++++++++++++++ 5 files changed, 1516 insertions(+), 22 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml create mode 100644 hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java create mode 100644 hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml new file mode 100644 index 00000000000..226bb7cd153 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5442 +title: "The ValidatorResourceFetcher will now resolve canonical URL references as well as simple local references." diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java index 171e5cfeb8e..176983551fb 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java @@ -327,18 +327,29 @@ public interface IFhirResourceDao extends IDao { /** * @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead + * @throws InvalidRequestException If a SearchParameter is not known to the server */ - IBundleProvider search(SearchParameterMap theParams); + IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException; - IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails); + /** + * * + * @throws InvalidRequestException If a SearchParameter is not known to the server + */ + IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails) + throws InvalidRequestException; + /** + * * + * @throws InvalidRequestException If a SearchParameter is not known to the server + */ IBundleProvider search( - SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse); + SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) + throws InvalidRequestException; /** * Search for IDs for processing a match URLs, etc. */ - default List searchForIds( + default List searchForIds( SearchParameterMap theParams, RequestDetails theRequest) { return searchForIds(theParams, theRequest, null); } @@ -350,7 +361,7 @@ public interface IFhirResourceDao extends IDao { * create/update, this is the resource being searched for * @since 5.5.0 */ - default List searchForIds( + default List searchForIds( SearchParameterMap theParams, RequestDetails theRequest, @Nullable IBaseResource theConditionalOperationTargetOrNull) { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java index d7fc7b40ab7..f988532989a 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java @@ -24,12 +24,14 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; -import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r5.elementmodel.Element; @@ -37,12 +39,11 @@ import org.hl7.fhir.r5.elementmodel.JsonParser; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.utilities.CanonicalPair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; +import java.util.List; import java.util.Locale; public class ValidatorResourceFetcher implements IValidatorResourceFetcher { @@ -50,22 +51,19 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class); private final FhirContext myFhirContext; - private final IValidationSupport myValidationSupport; private final DaoRegistry myDaoRegistry; private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper; public ValidatorResourceFetcher( FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) { myFhirContext = theFhirContext; - myValidationSupport = theValidationSupport; myDaoRegistry = theDaoRegistry; myVersionSpecificContextWrapper = - VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport); + VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport); } @Override - public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) - throws FHIRFormatError, DefinitionException, FHIRException, IOException { + public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException { IdType id = new IdType(theUrl); String resourceType = id.getResourceType(); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); @@ -74,9 +72,13 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { target = dao.read(id, (RequestDetails) appContext); } catch (ResourceNotFoundException e) { ourLog.info("Failed to resolve local reference: {}", theUrl); - return null; + try { + target = fetchByUrl(theUrl, dao, (RequestDetails) appContext); + } catch (ResourceNotFoundException e2) { + ourLog.info("Failed to find resource by URL: {}", theUrl); + return null; + } } - try { return new JsonParser(myVersionSpecificContextWrapper) .parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType); @@ -85,15 +87,40 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { } } + private IBaseResource fetchByUrl(String url, IFhirResourceDao dao, RequestDetails requestDetails) + throws ResourceNotFoundException { + CanonicalPair pair = new CanonicalPair(url); + SearchParameterMap searchParameterMap = new SearchParameterMap(); + searchParameterMap.add("url", new UriParam(pair.getUrl())); + String version = pair.getVersion(); + if (version != null && !version.isEmpty()) { + searchParameterMap.add("version", new TokenParam(version)); + } + List results = null; + try { + results = dao.search(searchParameterMap, requestDetails).getAllResources(); + } catch (InvalidRequestException e) { + ourLog.info("Resource does not support 'url' or 'version' Search Parameters"); + } + if (results != null && results.size() > 0) { + if (results.size() > 1) { + ourLog.warn( + String.format("Multiple results found for URL '%s', only the first will be considered.", url)); + } + return results.get(0); + } else { + throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url); + } + } + @Override public boolean resolveURL( - IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) - throws IOException, FHIRException { + IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) { return true; } @Override - public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws MalformedURLException, IOException { + public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException { throw new UnsupportedOperationException(Msg.code(577)); } @@ -104,8 +131,7 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { } @Override - public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) - throws URISyntaxException { + public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) { return null; } diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java new file mode 100644 index 00000000000..9009f6137fa --- /dev/null +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.jpa.validation; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.test.BaseTest; +import ca.uhn.fhir.util.ClasspathUtil; +import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; +import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.validation.instance.InstanceValidator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + + +public class ValidatorResourceFetcherTest extends BaseTest { + private static final FhirContext ourCtx = FhirContext.forR4(); + private static final DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx); + private static ValidatorResourceFetcher fetcher; + private static DaoRegistry mockDaoRegistry; + private static IFhirResourceDao mockResourceDao; + + @SuppressWarnings("unchecked") + @BeforeEach + public void before() { + mockDaoRegistry = mock(DaoRegistry.class); + mockResourceDao = mock(IFhirResourceDao.class); + fetcher = new ValidatorResourceFetcher(ourCtx, myDefaultValidationSupport, mockDaoRegistry); + } + + @Test + public void checkFetchByUrl() { + // setup mocks + String resource = ClasspathUtil.loadResource("/q_jon_with_url_version.json"); + doReturn(mockResourceDao).when(mockDaoRegistry).getResourceDao("Questionnaire"); + doThrow(new ResourceNotFoundException("Not Found")).when(mockResourceDao).read(any(),any()); + doReturn(new SimpleBundleProvider(List.of( + ourCtx.newJsonParser().parseResource(resource) + ))).when(mockResourceDao).search(any(),any()); + VersionSpecificWorkerContextWrapper wrappedWorkerContext = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myDefaultValidationSupport); + InstanceValidator v = new InstanceValidator( + wrappedWorkerContext, + new FhirInstanceValidator.NullEvaluationContext(), + new XVerExtensionManager(null)); + RequestDetails r = new SystemRequestDetails(); + // test + Element returnedResource = fetcher.fetch(v, r,"http://www.test-url-for-questionnaire.com/Questionnaire/test-id|1.0.0"); + assertNotNull(returnedResource); + } +} diff --git a/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json new file mode 100644 index 00000000000..7ca33e5e290 --- /dev/null +++ b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json @@ -0,0 +1,1388 @@ +{ + "resourceType": "Questionnaire", + "status": "draft", + "date": "2015-10-29", + "publisher": "Cancer Care Ontario", + "id": "test-id", + "telecom": [ + { + "system": "email", + "value": "jon.zammit@cancercare.on.ca" + } + ], + "title": "CT Lung for Cancer Staging Template - DRAFT (Short Version)", + "url": "http://www.test-url-for-questionnaire.com/Questionnaire/test-id", + "version": "1.0.0", + "item": [ + { + "text": "Patient with high suspicion of cancer as per the PEBC document (EBS #24-2) or radiological/laboratory tests suggesting cancer. Excluding: patients with synchronous lung primary, previous diagnosis of lung cancer, lung cancer surgery or therapy. New single lung primary only.", + "type": "display" + }, + { + "linkId": "root", + "type": "group", + "required": true, + "item": [ + { + "linkId": "g1", + "concept": [ + { + "system": "http://loinc.org", + "code": "55752-0", + "display": "Clinical Information" + } + ], + "text": "CLINICAL INFORMATION", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "1.1" + } + ], + "linkId": "1.1", + "text": "Patient Clinical Information", + "type": "text" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "1.2" + } + ], + "linkId": "1.2", + "text": "Previous Examination (Date and Modality)", + "type": "text" + } + ] + }, + { + "linkId": "g2", + "text": "IMAGING PROCEDURE DESCRIPTION", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.1" + } + ], + "linkId": "2.1", + "text": "Overall Image Quality:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "2.1a", + "display": "Adequate" + } + }, + { + "valueCoding": { + "code": "2.1b", + "display": "Suboptimal" + } + }, + { + "valueCoding": { + "code": "2.1c", + "display": "Non-diagnostic" + } + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.2" + } + ], + "linkId": "2.2", + "text": "Intravenous Contrast Used?", + "type": "boolean" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.3" + } + ], + "linkId": "2.3", + "text": "Additional Comments", + "type": "text" + } + ] + }, + { + "linkId": "g3", + "text": "FINDINGS", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3" + } + ], + "linkId": "g3.0", + "text": "T Category", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1" + } + ], + "linkId": "g3.1", + "text": "Location of Main Nodule/Mass (Primary tumor, or Reference tumor)", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1.1" + } + ], + "linkId": "q3.1.1", + "text": "Location of main nodule/mass:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "3.1.1a", + "display": "Peripheral" + } + }, + { + "valueCoding": { + "code": "3.1.1b", + "display": "Central*" + } + }, + { + "valueCoding": { + "code": "3.1.1c", + "display": "Both*" + } + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "q3.1.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.1.1b" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "q3.1.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.1.1c" + } + } + ] + } + ], + "linkId": "g3.1.1i", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + } + ], + "linkId": "q3.1.1i", + "text": "What is the distance of the nodule/mass to the carina?", + "type": "integer", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control", + "code": "unit" + } + ] + } + } + ], + "text": "mm", + "type": "display" + } + ] + }, + { + "linkId": "3.1.1i.image", + "text": "image", + "type": "string" + }, + { + "linkId": "3.1.1i.series", + "text": "series", + "type": "string" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1.2" + } + ], + "linkId": "3.1.2", + "text": "State the lobe(s) and segment(s) where the nodule/mass is located", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2" + } + ], + "linkId": "g3.2", + "text": "Size and characteristics of main nodule/mass", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.1" + } + ], + "linkId": "3.2.1", + "text": "Size of the nodule/mass:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "3.2.1a", + "display": "Solid nodule/mass" + } + }, + { + "valueCoding": { + "code": "3.2.1b", + "display": "Part-solid nodule/mass" + } + }, + { + "valueCoding": { + "code": "3.2.1c", + "display": "Pure Ground glass" + } + } + ], + "item": [ + { + "linkId": "g3.2.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.2.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.2.1a" + } + } + ] + } + ], + "linkId": "g3.2.1a", + "type": "group", + "item": [ + { + "linkId": "3.2.1a", + "text": "largest dimension:", + "type": "integer", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control", + "code": "unit" + } + ] + } + } + ], + "text": "mm", + "type": "display" + } + ] + }, + { + "linkId": "3.2.1a.image", + "text": "image", + "type": "string" + }, + { + "linkId": "3.2.1a.series", + "text": "series", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.2" + } + ], + "linkId": "3.2.2", + "text": "Plane in which the mass was measured:", + "type": "choice", + "option": [ + { + "code": "3.2.2a", + "display": "Axial" + }, + { + "code": "3.2.2b", + "display": "Coronal" + }, + { + "code": "3.2.2c", + "display": "Sagittal" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.3" + } + ], + "linkId": "3.2.3", + "text": "Other characteristics of the main nodule/mass:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3" + } + ], + "linkId": "g3.3", + "text": "Structures directly involved", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.1" + } + ], + "linkId": "3.3.1", + "text": "State if there is bronchial involvement:", + "type": "choice", + "option": [ + { + "code": "3.3.1a", + "display": "*Yes" + }, + { + "code": "3.3.1b", + "display": "No" + }, + { + "code": "3.3.1c", + "display": "Uncertain" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, answer i and ii.", + "type": "display" + }, + { + "linkId": "g3.3.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.1a" + } + } + ] + } + ], + "linkId": "g3.3.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + } + ], + "linkId": "3.3.1i", + "text": "Type of involvement:", + "type": "text" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "ii)" + } + ], + "linkId": "3.3.1ii", + "text": "State if there is endobronchial involvement:", + "type": "choice", + "option": [ + { + "code": "3.3.1.iia", + "display": "*Yes" + }, + { + "code": "3.3.1.iib", + "display": "No" + }, + { + "code": "3.3.1.iic", + "display": "Uncertain" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.1ii" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.1.iia" + } + } + ] + } + ], + "linkId": "3.3.1iic", + "text": "Describe:", + "type": "text" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.2" + } + ], + "linkId": "3.3.2", + "text": "Is there direct involvement of any other anatomical structures?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe all ipsilateral anatomical structures involved by the mass. If no, go to 3.3.3.", + "type": "display" + }, + { + "linkId": "g3.3.2.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.2" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "3.3.2.Yes", + "text": "Indicate and describe all ipsilateral anatomical structures involved by the mass:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "3.3.2.Yes.a", + "display": "Pleura" + }, + { + "code": "3.3.2.Yes.b", + "display": "Brachial Plexus" + }, + { + "code": "3.3.2.Yes.c", + "display": "Diaphragm" + }, + { + "code": "3.3.2.Yes.d", + "display": "Mediastinal fat" + }, + { + "code": "3.3.2.Yes.e", + "display": "Aorta" + }, + { + "code": "3.3.2.Yes.f", + "display": "Pulmonary Vessel" + }, + { + "code": "3.3.2.Yes.g", + "display": "Pericardium or Heart" + }, + { + "code": "3.3.2.Yes.h", + "display": "Mediastinal Vessels (including SVC)" + }, + { + "code": "3.3.2.Yes.i", + "display": "Trachea/carina" + }, + { + "code": "3.3.2.Yes.j", + "display": "Esophagus" + }, + { + "code": "3.3.2.Yes.k", + "display": "Trachea esophageal groove" + }, + { + "code": "3.3.2.Yes.l", + "display": "Vertebral Body" + }, + { + "code": "3.3.2.Yes.m", + "display": "Chest wall and Ribs" + }, + { + "code": "3.3.2.Yes.n", + "display": "Other structures" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.2.Yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.2.Yes.a" + } + } + ] + } + ], + "linkId": "3.3.2.Yes.a.text", + "text": "Pleura, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.3" + } + ], + "linkId": "3.3.3", + "text": "Are there additional suspicious pulmonary nodules?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe pulmonary nodules which apply. If no, go to 3.3.4.", + "type": "display" + }, + { + "linkId": "g3.3.3.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.3" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "3.3.3.Yes", + "text": "Indicate and describe pulmonary nodules which apply:", + "type": "choice", + "option": [ + { + "code": "3.3.3.Yes.a", + "display": "In the same lobe" + }, + { + "code": "3.3.3.Yes.b", + "display": "In a different lobe, same lung" + }, + { + "code": "3.3.3.Yes.c", + "display": "In the opposite lung (M1a)" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.3.Yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.3.Yes.a" + } + } + ] + } + ], + "linkId": "3.3.3.Yes.a.text", + "text": "In the same lobe, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.4" + } + ], + "linkId": "3.3.4", + "text": "Other notable intrathoracic findings (eg lymphangitis carcinomatosis):", + "type": "text" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4" + } + ], + "linkId": "4.0", + "text": "N Category", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4.1" + } + ], + "linkId": "4.1", + "text": "Are there enlarged lymph nodes?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the nodes which apply. If no, go to 4.2.", + "type": "display" + }, + { + "linkId": "g4.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "4.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "4.1.yes", + "text": "Nodes and Descriptions:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "4.1.yes.a", + "display": "1 Low Cervical, supraclavicular, and sternal notch nodes" + }, + { + "code": "4.1.yes.b", + "display": "2R Upper Paratracheal (right)" + }, + { + "code": "4.1.yes.c", + "display": "2L Upper paratracheal (left)" + }, + { + "code": "4.1.yes.d", + "display": "3a Pre-vascular" + }, + { + "code": "4.1.yes.e", + "display": "3p Retrotracheal" + }, + { + "code": "4.1.yes.f", + "display": "4R Lower paratracheal (right)" + }, + { + "code": "4.1.yes.g", + "display": "4L Upper paratracheal (left)" + }, + { + "code": "4.1.yes.h", + "display": "5 Subaortic" + }, + { + "code": "4.1.yes.i", + "display": "6 Para-aortic (ascending aorta or phrenic)" + }, + { + "code": "4.1.yes.j", + "display": "7 Subcarinal" + }, + { + "code": "4.1.yes.k", + "display": "8 Paraesophageal (below carina)" + }, + { + "code": "4.1.yes.l", + "display": "9 Pulmonary ligament" + }, + { + "code": "4.1.yes.m", + "display": "10 Hilar" + }, + { + "code": "4.1.yes.n", + "display": "11 Interlobar" + }, + { + "code": "4.1.yes.o", + "display": "12 Lobar" + }, + { + "code": "4.1.yes.p", + "display": "13 Segmental" + }, + { + "code": "4.1.yes.q", + "display": "14 Subsegmental" + }, + { + "code": "4.1.yes.r", + "display": "Other Nodes (axilla, sub-diaphragmatic)" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "4.1.yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "4.1.yes.a" + } + } + ] + } + ], + "linkId": "4.1.yes.description", + "text": "Description of node:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4.2" + } + ], + "linkId": "4.2", + "text": "Other notable findings:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "5" + } + ], + "linkId": "5.0", + "text": "M Category (Suspicious Extrathoracic Findings (M1b))", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "5.1" + } + ], + "linkId": "5.1", + "text": "Are there suspicious extrathoracic findings?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the structures below which apply. If no, go to 6.", + "type": "display" + }, + { + "linkId": "g5.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "5.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "5.1.yes", + "text": "Applicable Structures and Descriptions:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "5.1.yes.a", + "display": "Adrenals" + }, + { + "code": "5.1.yes.b", + "display": "Liver" + }, + { + "code": "5.1.yes.c", + "display": "Bone" + }, + { + "code": "5.1.yes.d", + "display": "Other" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "5.1.yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "5.1.yes.d" + } + } + ] + } + ], + "linkId": "5.1.yes.d.description", + "text": "Description of structures:", + "type": "string" + } + ] + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6" + } + ], + "linkId": "6.0", + "text": "Additional Findings", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6.1" + } + ], + "linkId": "6.1", + "text": "Are there additional findings?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the findings below which apply. If no, go to Impressions.", + "type": "display" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6.2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "6.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "6.2", + "text": "Findings and Descriptions:", + "type": "open-choice", + "repeats": true, + "option": [ + { + "code": "6.2a", + "display": "Emphysema" + }, + { + "code": "6.2b", + "display": "Fibrosis" + }, + { + "code": "6.2c", + "display": "Coronary artery classification" + }, + { + "code": "6.2d", + "display": "Asbestos related pleural disease" + }, + { + "code": "6.2e", + "display": "Interstitial lung disease" + }, + { + "code": "6.2f", + "display": "Atherosclerosis" + }, + { + "code": "6.2g", + "display": "Pulmonary Embolism" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "6.2" + }, + { + "url": "#answer", + "valueCoding": { + "code": "6.2a" + } + } + ] + } + ], + "linkId": "6.2a", + "text": "Pulmonary, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "linkId": "g7", + "text": "IMPRESSIONS", + "type": "group", + "item": [ + { + "linkId": "g7.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "7.1" + } + ], + "linkId": "7.1", + "text": "Impression/Summary:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "7.2" + } + ], + "linkId": "g7.2", + "text": "Radiologic Staging (TNM Version – 7th edition)", + "type": "group", + "item": [ + { + "linkId": "g7.20", + "text": "If this is a biopsy proven carcinoma, the preliminary radiologic stage is:", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-questionControl", + "valueCodeableConcept": { + "coding": [ + { + "code": "radio-button", + "display": "Radio Button" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode": "horizontal" + } + ], + "linkId": "7.2i", + "text": "Primary Tumour (T):", + "type": "choice", + "option": [ + { + "code": "T1a", + "display": "T1a" + }, + { + "code": "T1b", + "display": "T1b" + }, + { + "code": "T2", + "display": "T2" + }, + { + "code": "T2a", + "display": "T2a" + }, + { + "code": "T2b", + "display": "T2b" + }, + { + "code": "T3", + "display": "T3" + }, + { + "code": "T4", + "display": "T4" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "ii)" + } + ], + "linkId": "7.2ii", + "text": "Regional Lymph Nodes (N):", + "type": "choice", + "option": [ + { + "code": "NX", + "display": "NX" + }, + { + "code": "N0", + "display": "N0" + }, + { + "code": "N1", + "display": "N1" + }, + { + "code": "N2", + "display": "N2" + }, + { + "code": "N3", + "display": "N3" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "iii)" + } + ], + "linkId": "7.2iii", + "text": "Distant Metastasis (M):", + "type": "choice", + "option": [ + { + "code": "M0", + "display": "M0" + }, + { + "code": "M1", + "display": "M1" + }, + { + "code": "M1a", + "display": "M1a" + }, + { + "code": "M1b", + "display": "M1b" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} From ee414b73d9755ebfa9ba40714b8c837930bd971d Mon Sep 17 00:00:00 2001 From: Martha Mitran Date: Tue, 28 Nov 2023 13:12:05 -0800 Subject: [PATCH 48/86] Pass properties through to Remote Terminology Service on CodeSystem lookup (#5477) * Pass properties through to Remote Terminology Service on CodeSystem operation * Propagate list of property names throughout. Introduce a parameter object for the lookupCode method. Mark other lookupCode methods as deprecated. Add unit tests. * Update remote terminology service tests * Address code review comments * Fix unit tests * Address latest code review comments to update default methods * Address latest code review comments to update default methods - update fallback condition * Address latest code review comments to update default methods - update fallback condition --- .../context/support/IValidationSupport.java | 57 +++- .../context/support/LookupCodeRequest.java | 56 ++++ .../uhn/hapi/fhir/docs/ValidatorExamples.java | 5 +- ...e-terminology-service-for-code-system.yaml | 5 + .../jpa/dao/JpaResourceDaoCodeSystem.java | 41 ++- .../BaseJpaResourceProviderCodeSystem.java | 11 +- .../provider/ValueSetOperationProvider.java | 42 +-- .../ValueSetOperationProviderDstu2.java | 13 +- .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 25 +- ...ProviderDstu3CodeSystemPropertiesTest.java | 101 ++++++ .../ResourceProviderDstu3ValueSetTest.java | 14 +- ...rceProviderR4CodeSystemPropertiesTest.java | 91 ++++++ .../r4/ResourceProviderR4CodeSystemTest.java | 23 ++ ...rceProviderR4ValueSetNoVerCSNoVerTest.java | 8 +- .../jpa/term/TerminologySvcDeltaR4Test.java | 15 +- ...erminologySvcImplCurrentVersionR4Test.java | 13 +- ...rceProviderR5CodeSystemPropertiesTest.java | 89 ++++++ .../r5/ResourceProviderR5ValueSetTest.java | 8 +- .../CodeSystemLookupWithPropertiesUtil.java | 28 ++ ...rminologyDisplayPopulationInterceptor.java | 5 +- .../api/dao/IFhirResourceDaoCodeSystem.java | 10 + .../support/BaseValidationSupportWrapper.java | 8 +- .../support/CachingValidationSupport.java | 15 +- .../CommonCodeSystemsTerminologyService.java | 36 ++- ...oryTerminologyServerValidationSupport.java | 21 +- ...teTerminologyServiceValidationSupport.java | 33 +- ...ownCodeSystemWarningValidationSupport.java | 8 +- .../support/ValidationSupportChain.java | 27 +- ...ologyDisplayPopulationInterceptorTest.java | 10 - ...mmonCodeSystemsTerminologyServiceTest.java | 21 +- ...rminologyServiceValidationSupportTest.java | 297 +++++++----------- ...logyServiceValidationSupportDstu3Test.java | 189 +++++------ .../FhirInstanceValidatorR4Test.java | 8 +- .../FhirInstanceValidatorR4BTest.java | 35 ++- ...inologyServiceValidationSupportR5Test.java | 14 +- 35 files changed, 890 insertions(+), 492 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml create mode 100644 hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java create mode 100644 hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 27e0f5ddea5..3f9c4030c44 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -329,14 +329,16 @@ public interface IValidationSupport { } /** - * Look up a code using the system and code value + * Look up a code using the system and code value. + * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. * @param theSystem The CodeSystem URL * @param theCode The code - * @param theDisplayLanguage to filter out the designation by the display language. To return all designation, set this value to null. + * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null. */ + @Deprecated @Nullable default LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, @@ -348,12 +350,14 @@ public interface IValidationSupport { /** * Look up a code using the system and code value + * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. * @param theSystem The CodeSystem URL * @param theCode The code */ + @Deprecated @Nullable default LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { @@ -361,7 +365,26 @@ public interface IValidationSupport { } /** - * Returns true if the given valueset can be validated by the given + * Look up a code using the system, code and other parameters captured in {@link LookupCodeRequest}. + * @since 7.0.0 + * + * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to + * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. + * @param theLookupCodeRequest The parameters used to perform the lookup, including system and code. + */ + @Nullable + default LookupCodeResult lookupCode( + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + // TODO: can change to return null once the deprecated methods are removed + return lookupCode( + theValidationSupportContext, + theLookupCodeRequest.getSystem(), + theLookupCodeRequest.getCode(), + theLookupCodeRequest.getDisplayLanguage()); + } + + /** + * Returns true if the given ValueSet can be validated by the given * validation support module * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to @@ -554,6 +577,11 @@ public interface IValidationSupport { } class CodeValidationResult { + public static final String SOURCE_DETAILS = "sourceDetails"; + public static final String RESULT = "result"; + public static final String MESSAGE = "message"; + public static final String DISPLAY = "display"; + private String myCode; private String myMessage; private IssueSeverity mySeverity; @@ -682,6 +710,23 @@ public interface IValidationSupport { setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase())); return this; } + + public IBaseParameters toParameters(FhirContext theContext) { + IBaseParameters retVal = ParametersUtil.newInstance(theContext); + + ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, isOk()); + if (isNotBlank(getMessage())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, getMessage()); + } + if (isNotBlank(getDisplay())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, getDisplay()); + } + if (isNotBlank(getSourceDetails())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails()); + } + + return retVal; + } } class ValueSetExpansionOutcome { @@ -814,7 +859,7 @@ public interface IValidationSupport { } public IBaseParameters toParameters( - FhirContext theContext, List> theProperties) { + FhirContext theContext, List> thePropertyNames) { IBaseParameters retVal = ParametersUtil.newInstance(theContext); if (isNotBlank(getCodeSystemDisplayName())) { @@ -829,8 +874,8 @@ public interface IValidationSupport { if (myProperties != null) { Set properties = Collections.emptySet(); - if (theProperties != null) { - properties = theProperties.stream() + if (thePropertyNames != null) { + properties = thePropertyNames.stream() .map(IPrimitiveType::getValueAsString) .collect(Collectors.toSet()); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java new file mode 100644 index 00000000000..dc7074bba25 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.context.support; + +import java.util.Collection; +import java.util.Collections; + +/** + * Represents parameters which can be passed to the $lookup operation for codes. + * @since 7.0.0 + */ +public class LookupCodeRequest { + private final String mySystem; + private final String myCode; + private String myDisplayLanguage; + private Collection myPropertyNames; + + /** + * @param theSystem The CodeSystem URL + * @param theCode The code + */ + public LookupCodeRequest(String theSystem, String theCode) { + mySystem = theSystem; + myCode = theCode; + } + + /** + * @param theSystem The CodeSystem URL + * @param theCode The code + * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null. + * @param thePropertyNames The collection of properties to be returned in the output. If no properties are specified, the implementor chooses what to return. + */ + public LookupCodeRequest( + String theSystem, String theCode, String theDisplayLanguage, Collection thePropertyNames) { + this(theSystem, theCode); + myDisplayLanguage = theDisplayLanguage; + myPropertyNames = thePropertyNames; + } + + public String getSystem() { + return mySystem; + } + + public String getCode() { + return myCode; + } + + public String getDisplayLanguage() { + return myDisplayLanguage; + } + + public Collection getPropertyNames() { + if (myPropertyNames == null) { + return Collections.emptyList(); + } + return myPropertyNames; + } +} diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java index ed304aa3b48..d75bd9d5064 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.parser.IParser; @@ -308,9 +309,7 @@ public class ValidatorExamples { @Override public LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + @Nonnull LookupCodeRequest validationSupportParameterObject) { // TODO: implement (or return null if your implementation does not support this function) return null; } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml new file mode 100644 index 00000000000..f4b02e23951 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5476 +title: "A new method on the IValidationSupport interface called lookupCode(LookupCodeRequest) has been added. +This method will replace the existing lookupCode methods, which are now deprecated." diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java index e4b128f608e..12a4f01a947 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; @@ -41,6 +42,7 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import org.apache.commons.collections4.CollectionUtils; import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; @@ -52,8 +54,10 @@ import org.hl7.fhir.r4.model.Coding; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.PostConstruct; @@ -125,8 +129,33 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF IBaseCoding theCoding, IPrimitiveType theDisplayLanguage, RequestDetails theRequestDetails) { + return lookupCode( + theCode, + theSystem, + theCoding, + theDisplayLanguage, + CollectionUtils.emptyCollection(), + theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode( + IPrimitiveType theCode, + IPrimitiveType theSystem, + IBaseCoding theCoding, + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames, + RequestDetails theRequestDetails) { return doLookupCode( - myFhirContext, myTerser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage); + myFhirContext, + myTerser, + myValidationSupport, + theCode, + theSystem, + theCoding, + theDisplayLanguage, + thePropertyNames); } @Override @@ -285,7 +314,8 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF IPrimitiveType theCode, IPrimitiveType theSystem, IBaseCoding theCoding, - IPrimitiveType theDisplayLanguage) { + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames) { boolean haveCoding = theCoding != null && isNotBlank(extractCodingSystem(theCoding)) && isNotBlank(extractCodingCode(theCoding)); @@ -323,11 +353,16 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF ourLog.info("Looking up {} / {}", system, code); + Collection propertyNames = CollectionUtils.emptyIfNull(thePropertyNames).stream() + .map(IPrimitiveType::getValueAsString) + .collect(Collectors.toSet()); + if (theValidationSupport.isCodeSystemSupported(new ValidationSupportContext(theValidationSupport), system)) { ourLog.info("Code system {} is supported", system); IValidationSupport.LookupCodeResult retVal = theValidationSupport.lookupCode( - new ValidationSupportContext(theValidationSupport), system, code, displayLanguage); + new ValidationSupportContext(theValidationSupport), + new LookupCodeRequest(system, code, displayLanguage, propertyNames)); if (retVal != null) { return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java index 1929635de94..85b88f5f0bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java @@ -45,7 +45,6 @@ import java.util.Optional; import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; -import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.toValidateCodeResult; import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseJpaResourceProviderCodeSystem extends BaseJpaResourceProvider { @@ -65,6 +64,7 @@ public abstract class BaseJpaResourceProviderCodeSystem @OperationParam(name = "version", typeName = "string", min = 0), @OperationParam(name = "display", typeName = "string", min = 1), @OperationParam(name = "abstract", typeName = "boolean", min = 1), + @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") }) public IBaseParameters lookup( HttpServletRequest theServletRequest, @@ -75,7 +75,7 @@ public abstract class BaseJpaResourceProviderCodeSystem @OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") - List> theProperties, + List> thePropertyNames, RequestDetails theRequestDetails) { startRequest(theServletRequest); @@ -83,9 +83,10 @@ public abstract class BaseJpaResourceProviderCodeSystem IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); IValidationSupport.LookupCodeResult result; applyVersionToSystem(theSystem, theVersion); - result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); + result = dao.lookupCode( + theCode, theSystem, theCoding, theDisplayLanguage, thePropertyNames, theRequestDetails); result.throwNotFoundIfAppropriate(); - return result.toParameters(theRequestDetails.getFhirContext(), theProperties); + return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); } finally { endRequest(theServletRequest); } @@ -191,7 +192,7 @@ public abstract class BaseJpaResourceProviderCodeSystem theCodeableConcept, theRequestDetails); } - return toValidateCodeResult(getContext(), result); + return result.toParameters(getContext()); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java index 99189c92154..2bb327cf9c7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java @@ -19,7 +19,6 @@ */ package ca.uhn.fhir.jpa.provider; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; @@ -61,10 +60,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValueSetOperationProvider extends BaseJpaProvider { private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class); - public static final String SOURCE_DETAILS = "sourceDetails"; - public static final String RESULT = "result"; - public static final String MESSAGE = "message"; - public static final String DISPLAY = "display"; @Autowired protected IValidationSupport myValidationSupport; @@ -149,10 +144,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider { idempotent = true, typeName = "ValueSet", returnParameters = { - @OperationParam(name = RESULT, typeName = "boolean", min = 1), - @OperationParam(name = MESSAGE, typeName = "string"), - @OperationParam(name = DISPLAY, typeName = "string"), - @OperationParam(name = SOURCE_DETAILS, typeName = "string") + @OperationParam(name = CodeValidationResult.RESULT, typeName = "boolean", min = 1), + @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string"), + @OperationParam(name = CodeValidationResult.DISPLAY, typeName = "string"), + @OperationParam(name = CodeValidationResult.SOURCE_DETAILS, typeName = "string") }) public IBaseParameters validateCode( HttpServletRequest theServletRequest, @@ -164,7 +159,8 @@ public class ValueSetOperationProvider extends BaseJpaProvider { @OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType theSystem, @OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string") IPrimitiveType theSystemVersion, - @OperationParam(name = DISPLAY, min = 0, max = 1, typeName = "string") IPrimitiveType theDisplay, + @OperationParam(name = CodeValidationResult.DISPLAY, min = 0, max = 1, typeName = "string") + IPrimitiveType theDisplay, @OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding, @OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") ICompositeType theCodeableConcept, @@ -228,7 +224,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { theCodeableConcept, theRequestDetails); } - return toValidateCodeResult(getContext(), result); + return result.toParameters(getContext()); } finally { endRequest(theServletRequest); } @@ -256,7 +252,9 @@ public class ValueSetOperationProvider extends BaseJpaProvider { name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION, idempotent = false, typeName = "ValueSet", - returnParameters = {@OperationParam(name = MESSAGE, typeName = "string", min = 1, max = 1)}) + returnParameters = { + @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string", min = 1, max = 1) + }) public IBaseParameters invalidateValueSetExpansion( @IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) { startRequest(theServletRequest); @@ -265,7 +263,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails); IBaseParameters retVal = ParametersUtil.newInstance(getContext()); - ParametersUtil.addParameterToParametersString(getContext(), retVal, MESSAGE, outcome); + ParametersUtil.addParameterToParametersString(getContext(), retVal, CodeValidationResult.MESSAGE, outcome); return retVal; } finally { @@ -326,22 +324,4 @@ public class ValueSetOperationProvider extends BaseJpaProvider { return options; } - - public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) { - IBaseParameters retVal = ParametersUtil.newInstance(theContext); - - ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, theResult.isOk()); - if (isNotBlank(theResult.getMessage())) { - ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, theResult.getMessage()); - } - if (isNotBlank(theResult.getDisplay())) { - ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, theResult.getDisplay()); - } - if (isNotBlank(theResult.getSourceDetails())) { - ParametersUtil.addParameterToParametersString( - theContext, retVal, SOURCE_DETAILS, theResult.getSourceDetails()); - } - - return retVal; - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java index 372b030629b..0480f0290d2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java @@ -127,7 +127,7 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider { @OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") - List> theProperties, + List> thePropertyNames, RequestDetails theRequestDetails) { startRequest(theServletRequest); @@ -137,9 +137,16 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider { FhirTerser terser = getContext().newTerser(); result = JpaResourceDaoCodeSystem.doLookupCode( - getContext(), terser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage); + getContext(), + terser, + myValidationSupport, + theCode, + theSystem, + theCoding, + theDisplayLanguage, + thePropertyNames); result.throwNotFoundIfAppropriate(); - return result.toParameters(theRequestDetails.getFhirContext(), theProperties); + return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 73f95569ba6..9372b9b03b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; @@ -90,6 +91,7 @@ import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.collect.ArrayListMultimap; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -147,7 +149,6 @@ import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.CollectionUtils; import org.springframework.util.comparator.Comparators; import java.util.ArrayList; @@ -2575,10 +2576,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes); } - protected IValidationSupport.LookupCodeResult lookupCode( - String theSystem, String theCode, String theDisplayLanguage) { + @Override + public IValidationSupport.LookupCodeResult lookupCode( + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); return txTemplate.execute(t -> { + final String theSystem = theLookupCodeRequest.getSystem(); + final String theCode = theLookupCodeRequest.getCode(); Optional codeOpt = findCode(theSystem, theCode); if (codeOpt.isPresent()) { TermConcept code = codeOpt.get(); @@ -2593,7 +2597,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { for (TermConceptDesignation next : code.getDesignations()) { // filter out the designation based on displayLanguage if any - if (isDisplayLanguageMatch(theDisplayLanguage, next.getLanguage())) { + if (isDisplayLanguageMatch(theLookupCodeRequest.getDisplayLanguage(), next.getLanguage())) { IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); designation.setLanguage(next.getLanguage()); designation.setUseSystem(next.getUseSystem()); @@ -2604,7 +2608,11 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } } + final Collection propertyNames = theLookupCodeRequest.getPropertyNames(); for (TermConceptProperty next : code.getProperties()) { + if (ObjectUtils.isNotEmpty(propertyNames) && !propertyNames.contains(next.getKey())) { + continue; + } if (next.getType() == TermConceptPropertyTypeEnum.CODING) { IValidationSupport.CodingConceptProperty property = new IValidationSupport.CodingConceptProperty( @@ -3117,15 +3125,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { return isValueSetPreExpandedForCodeValidation(valueSetR4); } - @Override - public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { - return lookupCode(theSystem, theCode, theDisplayLanguage); - } - private static class TermCodeSystemVersionDetails { private final long myPid; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..66eaa679850 --- /dev/null +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java @@ -0,0 +1,101 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderDstu3CodeSystemPropertiesTest extends BaseResourceProviderDstu3Test { + + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode); + CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)); + concept.addProperty(propertyComponent); + propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)); + concept.addProperty(propertyComponent); + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + Iterator paramIterator = parameters.getParameter().iterator(); + Parameters.ParametersParameterComponent parameter = null; + while (paramIterator.hasNext()) { + Parameters.ParametersParameterComponent currentParameter = paramIterator.next(); + if (currentParameter.getName().equals("property")) { + parameter = currentParameter; + break; + } + } + + if (theExpectedReturnedProperties.isEmpty()) { + assertNull(parameter); + return; + } + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + CodeSystem.ConceptPropertyComponent property = propertyIterator.next(); + assertNotNull(parameter); + + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getValue()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + + if (paramIterator.hasNext()) { + parameter = paramIterator.next(); + } + } + } +} diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 09ce4621a13..5bee62a6211 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.dstu3; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -9,7 +10,6 @@ import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -790,13 +790,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } @@ -820,13 +820,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..7338a0b4174 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java @@ -0,0 +1,91 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderR4CodeSystemPropertiesTest extends BaseResourceProviderR4Test { + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode); + CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)); + concept.addProperty(propertyComponent); + propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)); + concept.addProperty(propertyComponent); + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + + if (theExpectedReturnedProperties.isEmpty()) { + assertFalse(parameters.hasParameter("property")); + return; + } + + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + CodeSystem.ConceptPropertyComponent property = propertyIterator.next(); + + assertTrue(parameterPropertyIterator.hasNext()); + Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 24958f4153b..06693388bee 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -138,6 +138,29 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } + @Test + public void testLookupOperationByCodeAndSystemWithPropertiesBuiltInCode() { + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .execute(); + + String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + @Test public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() { try { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java index 3ac4d4ed784..7b69cc6a741 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -14,7 +15,6 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -1295,13 +1295,13 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index dd0fe20bf61..7ab96037c26 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -369,7 +370,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals("http://foo", outcome.getUrl()); assertEquals(CodeSystem.CodeSystemContentMode.NOTPRESENT, outcome.getContent()); - IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "CBC", null); + IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), new LookupCodeRequest("http://foo", "CBC")); assertEquals("Complete Blood Count", lookup.getCodeDisplay()); } @@ -433,7 +434,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { UploadStatistics outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); // Add codes again with different display delta = new CustomTerminologySet(); @@ -441,12 +443,14 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { delta.addRootConcept("codeb", "CODEB1"); outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); // Add codes again with no changes outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); } @Test @@ -481,7 +485,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { .setValue(new Coding("http://snomed.info", "1234567", "Choked on large meal (finding)")); myCodeSystemDao.create(cs, mySrd); - IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo/cs", "lunch", null); + IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo/cs", "lunch")); assertEquals(true, result.isFound()); assertEquals("lunch", result.getSearchedForCode()); assertEquals("http://foo/cs", result.getSearchedForSystem()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java index 387609117bf..17cdf197835 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.config.JpaConfig; import ca.uhn.fhir.jpa.entity.TermCodeSystem; @@ -214,13 +215,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { private void validateValueLookup(String theCurrentVersion, Collection allVersions) { IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultNoVer); String expectedNoVer = prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedNoVer, resultNoVer.getCodeDisplay()); IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultWithVer); String expectedWithVer = prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedWithVer, resultWithVer.getCodeDisplay()); @@ -231,15 +232,15 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { private void lookupForVersion(String theVersion) { IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion, - VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultNoVer); String expectedNoVer = prefixWithVersion(theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedNoVer, resultNoVer.getCodeDisplay()); IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion, - VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultWithVer); String expectedWithVer = prefixWithVersion(theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedWithVer, resultWithVer.getCodeDisplay()); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..e8b5be13fb4 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java @@ -0,0 +1,89 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderR5CodeSystemPropertiesTest extends BaseResourceProviderR5Test { + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode); + CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)); + concept.addProperty(propertyComponent); + propertyComponent = new CodeSystem.ConceptPropertyComponent() + .setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)); + concept.addProperty(propertyComponent); + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + + if (theExpectedReturnedProperties.isEmpty()) { + assertFalse(parameters.hasParameter("property")); + return; + } + + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + CodeSystem.ConceptPropertyComponent property = propertyIterator.next(); + + assertTrue(parameterPropertyIterator.hasNext()); + Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index 6d9b8a299c7..a40eddf4e05 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r5; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -12,7 +13,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -1229,13 +1229,13 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java new file mode 100644 index 00000000000..feedbd05f0e --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java @@ -0,0 +1,28 @@ +package ca.uhn.fhir.jpa.provider; + +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class CodeSystemLookupWithPropertiesUtil { + public static final String ourCodeSystemId = "CodeSystem-Example", + ourCodeSystemUrl = "http://example/" + ourCodeSystemId; + public static final String ourCode = "Code-WithProperties"; + public static final String ourPropertyA = "Property-A", ourPropertyB = "Property-B"; + public static final String ourPropertyValueA = "Value-A", ourPropertyValueB = "Value-B"; + + public static Stream parametersLookupWithProperties() { + return Stream.of( + arguments(List.of(ourPropertyB), List.of(ourPropertyB)), + arguments(List.of(ourPropertyA, ourPropertyB), List.of(ourPropertyA, ourPropertyB)), + arguments(List.of(ourPropertyB, ourPropertyA), List.of(ourPropertyB, ourPropertyA)), + arguments(List.of(ourPropertyA, ourPropertyA), List.of(ourPropertyA, ourPropertyA)), + arguments(List.of(ourPropertyB, "ABC"), List.of(ourPropertyB)), + arguments(List.of("ABC", ourPropertyA), List.of(ourPropertyA)), + arguments(List.of("ABC"), Collections.emptyList())); + } +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java index c6a7c901ee6..e86facffe09 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Pointcut; @@ -122,8 +123,8 @@ public class ResponseTerminologyDisplayPopulationInterceptor extends BaseRespons ValidationSupportContext validationSupportContext = new ValidationSupportContext(myValidationSupport); if (myValidationSupport.isCodeSystemSupported(validationSupportContext, system)) { - IValidationSupport.LookupCodeResult lookupCodeResult = - myValidationSupport.lookupCode(validationSupportContext, system, code); + IValidationSupport.LookupCodeResult lookupCodeResult = myValidationSupport.lookupCode( + validationSupportContext, new LookupCodeRequest(system, code)); if (lookupCodeResult != null && lookupCodeResult.isFound()) { String newDisplay = lookupCodeResult.getCodeDisplay(); IPrimitiveType newString = myStringDefinition.newInstance(newDisplay); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index bf323ac8226..3ac4d312cd1 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -32,6 +32,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.springframework.transaction.annotation.Transactional; +import java.util.Collection; import java.util.List; import javax.annotation.Nonnull; @@ -55,6 +56,15 @@ public interface IFhirResourceDaoCodeSystem extends IFh IPrimitiveType theDisplayLanguage, RequestDetails theRequestDetails); + @Nonnull + IValidationSupport.LookupCodeResult lookupCode( + IPrimitiveType theCode, + IPrimitiveType theSystem, + IBaseCoding theCoding, + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames, + RequestDetails theRequestDetails); + SubsumesResult subsumes( IPrimitiveType theCodeA, IPrimitiveType theCodeB, diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java index e14a686e208..ecd220a3b98 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/BaseValidationSupportWrapper.java @@ -3,6 +3,7 @@ package org.hl7.fhir.common.hapi.validation.support; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; @@ -98,11 +99,8 @@ public abstract class BaseValidationSupportWrapper extends BaseValidationSupport @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { - return myWrap.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage); + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + return myWrap.lookupCode(theValidationSupportContext, theLookupCodeRequest); } @Override diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java index 873a854f1f3..82d86f234fb 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CachingValidationSupport.java @@ -3,6 +3,7 @@ package org.hl7.fhir.common.hapi.validation.support; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; @@ -195,15 +196,13 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { - String key = "lookupCode " + theSystem + " " + theCode + " " + defaultIfBlank(theDisplayLanguage, "NO_LANG"); + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + String key = "lookupCode " + theLookupCodeRequest.getSystem() + " " + + theLookupCodeRequest.getCode() + + " " + defaultIfBlank(theLookupCodeRequest.getDisplayLanguage(), "NO_LANG") + + " " + theLookupCodeRequest.getPropertyNames().toString(); return loadFromCache( - myLookupCodeCache, - key, - t -> super.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage)); + myLookupCodeCache, key, t -> super.lookupCode(theValidationSupportContext, theLookupCodeRequest)); } @Override diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index af9cd737566..f334cbcf29b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.util.ClasspathUtil; @@ -221,7 +222,8 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { @Nullable public CodeValidationResult validateLookupCode( ValidationSupportContext theValidationSupportContext, String theCode, String theSystem) { - LookupCodeResult lookupResult = lookupCode(theValidationSupportContext, theSystem, theCode); + LookupCodeResult lookupResult = + lookupCode(theValidationSupportContext, new LookupCodeRequest(theSystem, theCode)); CodeValidationResult validationResult = null; if (lookupResult != null) { if (lookupResult.isFound()) { @@ -240,18 +242,18 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + final String code = theLookupCodeRequest.getCode(); + final String system = theLookupCodeRequest.getSystem(); + Map map; - switch (theSystem) { + switch (system) { case LANGUAGES_CODESYSTEM_URL: - return lookupLanguageCode(theCode); + return lookupLanguageCode(code); case UCUM_CODESYSTEM_URL: - return lookupUcumCode(theCode); + return lookupUcumCode(code); case MIMETYPES_CODESYSTEM_URL: - return lookupMimetypeCode(theCode); + return lookupMimetypeCode(code); case COUNTRIES_CODESYSTEM_URL: map = ISO_3166_CODES; break; @@ -265,11 +267,11 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { return null; } - String display = map.get(theCode); + String display = map.get(code); if (isNotBlank(display)) { LookupCodeResult retVal = new LookupCodeResult(); - retVal.setSearchedForCode(theCode); - retVal.setSearchedForSystem(theSystem); + retVal.setSearchedForCode(code); + retVal.setSearchedForSystem(system); retVal.setFound(true); retVal.setCodeDisplay(display); return retVal; @@ -277,10 +279,10 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { // If we get here it means we know the codesystem but the code was bad LookupCodeResult retVal = new LookupCodeResult(); - retVal.setSearchedForCode(theCode); - retVal.setSearchedForSystem(theSystem); + retVal.setSearchedForCode(code); + retVal.setSearchedForSystem(system); retVal.setFound(false); - retVal.setErrorMessage("Code '" + theCode + "' is not valid for system: " + theSystem); + retVal.setErrorMessage("Code '" + code + "' is not valid for system: " + system); return retVal; } @@ -384,7 +386,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { String language = theNext.get("Subtag").asText(); ArrayNode descriptions = (ArrayNode) theNext.get("Description"); String description = null; - if (descriptions.size() > 0) { + if (!descriptions.isEmpty()) { description = descriptions.get(0).asText(); } theLanguagesMap.put(language, description); @@ -403,7 +405,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { @Nonnull private LookupCodeResult lookupUcumCode(String theCode) { InputStream input = ClasspathUtil.loadResourceAsStream("/ucum-essence.xml"); - String outcome = null; + String outcome; LookupCodeResult retVal = new LookupCodeResult(); retVal.setSearchedForCode(theCode); retVal.setSearchedForSystem(UCUM_CODESYSTEM_URL); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java index a2a709c0be6..2fa1c80befc 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/InMemoryTerminologyServerValidationSupport.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; @@ -611,16 +612,20 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + final String code = theLookupCodeRequest.getCode(); + final String system = theLookupCodeRequest.getSystem(); CodeValidationResult codeValidationResult = validateCode( - theValidationSupportContext, new ConceptValidationOptions(), theSystem, theCode, null, null); + theValidationSupportContext, + new ConceptValidationOptions(), + system, + code, + theLookupCodeRequest.getDisplayLanguage(), + null); if (codeValidationResult == null) { return null; } - return codeValidationResult.asLookupCodeResult(theSystem, theCode); + return codeValidationResult.asLookupCodeResult(system, code); } @Nullable @@ -927,9 +932,7 @@ public class InMemoryTerminologyServerValidationSupport implements IValidationSu .getRootValidationSupport() .lookupCode( theValidationSupportContext, - includeOrExcludeConceptSystemUrl, - theWantCode, - null); + new LookupCodeRequest(includeOrExcludeConceptSystemUrl, theWantCode)); if (lookup != null) { ableToHandleCode = true; if (lookup.isFound()) { diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index a889d36f05e..3bd5d2db48b 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; @@ -183,11 +184,11 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { - Validate.notBlank(theCode, "theCode must be provided"); + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + final String code = theLookupCodeRequest.getCode(); + final String system = theLookupCodeRequest.getSystem(); + final String displayLanguage = theLookupCodeRequest.getDisplayLanguage(); + Validate.notBlank(code, "theCode must be provided"); IGenericClient client = provideClient(); FhirContext fhirContext = client.getFhirContext(); @@ -197,17 +198,20 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup case DSTU3: case R4: IBaseParameters params = ParametersUtil.newInstance(fhirContext); - ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode); - if (!StringUtils.isEmpty(theSystem)) { - ParametersUtil.addParameterToParametersString(fhirContext, params, "system", theSystem); + ParametersUtil.addParameterToParametersString(fhirContext, params, "code", code); + if (!StringUtils.isEmpty(system)) { + ParametersUtil.addParameterToParametersString(fhirContext, params, "system", system); } - if (!StringUtils.isEmpty(theDisplayLanguage)) { - ParametersUtil.addParameterToParametersString(fhirContext, params, "language", theDisplayLanguage); + if (!StringUtils.isEmpty(displayLanguage)) { + ParametersUtil.addParameterToParametersString(fhirContext, params, "language", displayLanguage); } - Class codeSystemClass = + for (String propertyName : theLookupCodeRequest.getPropertyNames()) { + ParametersUtil.addParameterToParametersString(fhirContext, params, "property", propertyName); + } + Class codeSystemClass = myCtx.getResourceDefinition("CodeSystem").getImplementingClass(); IBaseParameters outcome = client.operation() - .onType((Class) codeSystemClass) + .onType(codeSystemClass) .named("$lookup") .withParameters(params) .useHttpGet() @@ -216,10 +220,9 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup switch (fhirVersion) { case DSTU3: return generateLookupCodeResultDSTU3( - theCode, theSystem, (org.hl7.fhir.dstu3.model.Parameters) outcome); + code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome); case R4: - return generateLookupCodeResultR4( - theCode, theSystem, (org.hl7.fhir.r4.model.Parameters) outcome); + return generateLookupCodeResultR4(code, system, (org.hl7.fhir.r4.model.Parameters) outcome); } } break; diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java index 0b4be8d9006..f8493bbaf03 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/UnknownCodeSystemWarningValidationSupport.java @@ -2,6 +2,7 @@ package org.hl7.fhir.common.hapi.validation.support; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -51,12 +52,9 @@ public class UnknownCodeSystemWarningValidationSupport extends BaseValidationSup @Nullable @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { // filters out error/fatal - if (canValidateCodeSystem(theValidationSupportContext, theSystem)) { + if (canValidateCodeSystem(theValidationSupportContext, theLookupCodeRequest.getSystem())) { return new LookupCodeResult().setFound(true); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java index 2b31d6852d3..65b6245fb29 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/ValidationSupportChain.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; @@ -416,20 +417,26 @@ public class ValidationSupportChain implements IValidationSupport { @Override public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theValidationSupportContext, theSystem)) { - LookupCodeResult lookupCodeResult = - next.lookupCode(theValidationSupportContext, theSystem, theCode, theDisplayLanguage); + final String system = theLookupCodeRequest.getSystem(); + final String code = theLookupCodeRequest.getCode(); + final String displayLanguage = theLookupCodeRequest.getDisplayLanguage(); + if (next.isCodeSystemSupported(theValidationSupportContext, system)) { + LookupCodeResult lookupCodeResult = next.lookupCode(theValidationSupportContext, theLookupCodeRequest); + if (lookupCodeResult == null) { + /* + This branch has been added as a fall-back mechanism for supporting lookupCode + methods marked as deprecated in interface IValidationSupport. + */ + lookupCodeResult = next.lookupCode(theValidationSupportContext, system, code, displayLanguage); + } if (ourLog.isDebugEnabled()) { ourLog.debug( "Code {}|{}{} {} by {}", - theSystem, - theCode, - isBlank(theDisplayLanguage) ? "" : " (" + theDisplayLanguage + ")", + system, + code, + isBlank(displayLanguage) ? "" : " (" + theLookupCodeRequest.getDisplayLanguage() + ")", lookupCodeResult != null && lookupCodeResult.isFound() ? "found" : "not found", next.getName()); } diff --git a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java index 2b476d65a31..99f5a4c00e6 100644 --- a/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java +++ b/hapi-fhir-validation/src/test/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptorTest.java @@ -138,16 +138,6 @@ public class ResponseTerminologyDisplayPopulationInterceptorTest { public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) { return true; } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) { - return null; - } - - @Override - public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { - return lookupCode(theValidationSupportContext, theSystem, theCode, null); - } } } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java index dc0d72961a0..cb8c812949c 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.i18n.Msg; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; @@ -31,35 +32,35 @@ public class CommonCodeSystemsTerminologyServiceTest { @Test public void testUcum_LookupCode_Good() { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "Cel"); + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "Cel")); assert outcome != null; assertTrue(outcome.isFound()); } @Test public void testUcum_LookupCode_Good2() { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "kg/m2"); + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "kg/m2")); assert outcome != null; assertTrue(outcome.isFound()); } @Test public void testUcum_LookupCode_Bad() { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://unitsofmeasure.org", "AAAAA"); + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://unitsofmeasure.org", "AAAAA")); assert outcome != null; assertFalse(outcome.isFound()); } @Test public void testUcum_LookupCode_UnknownSystem() { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), "http://foo", "AAAAA", null); + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(newSupport(), new LookupCodeRequest("http://foo", "AAAAA")); assertNull(outcome); } @Test public void lookupCode_languageOnlyLookup_isCaseInsensitive() { - IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "SGN", "Sign Languages"); - IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "sgn", "Sign Languages"); + IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "SGN", "Sign Languages", null)); + IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "sgn", "Sign Languages", null)); assertNotNull(outcomeUpper); assertNotNull(outcomeLower); assertTrue(outcomeLower.isFound()); @@ -68,8 +69,8 @@ public class CommonCodeSystemsTerminologyServiceTest { @Test public void lookupCode_languageAndRegionLookup_isCaseInsensitive() { - IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "EN-US", "English"); - IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "en-us", "English"); + IValidationSupport.LookupCodeResult outcomeUpper = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "EN-US", "English", null)); + IValidationSupport.LookupCodeResult outcomeLower = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "en-us", "English", null)); assertNotNull(outcomeUpper); assertNotNull(outcomeLower); assertTrue(outcomeLower.isFound()); @@ -131,14 +132,14 @@ public class CommonCodeSystemsTerminologyServiceTest { @Test public void testLanguages_CommonLanguagesVs_OnlyLanguage_NoRegion() { - IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl"); + IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "nl")); assertTrue(nl.isFound()); assertEquals("Dutch", nl.getCodeDisplay()); } @Test public void testLanguages_CommonLanguagesVs_LanguageAndRegion() { - IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl-NL"); + IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), new LookupCodeRequest("urn:ietf:bcp:47", "nl-NL")); assertTrue(nl.isFound()); assertEquals("Dutch Netherlands", nl.getCodeDisplay()); } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java index 99e8b754c3a..ebbf218b0e8 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.TranslateConceptResult; import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.parser.IJsonLikeParser; import ca.uhn.fhir.rest.annotation.IdParam; @@ -24,6 +25,7 @@ import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.ParametersUtil; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeSystem; @@ -47,11 +49,14 @@ import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Set; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -60,8 +65,7 @@ public class RemoteTerminologyServiceValidationSupportTest { private static final String DISPLAY = "DISPLAY"; private static final String LANGUAGE = "en"; private static final String CODE_SYSTEM = "CODE_SYS"; - private static final String CODE_SYSTEM_VERSION = "2.1"; - private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12"; + private static final String CODE_SYSTEM_NAME = "Code System"; private static final String CODE = "CODE"; private static final String VALUE_SET_URL = "http://value.set/url"; private static final String TARGET_SYSTEM = "http://target.system/url"; @@ -77,10 +81,10 @@ public class RemoteTerminologyServiceValidationSupportTest { private static final String ERROR_MESSAGE = "This is an error message"; private static final String SUCCESS_MESSAGE = "This is a success message"; - private static FhirContext ourCtx = FhirContext.forR4Cached(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); @RegisterExtension - public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx); + public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); private MyValueSetProvider myValueSetProvider; private RemoteTerminologyServiceValidationSupport mySvc; @@ -90,15 +94,15 @@ public class RemoteTerminologyServiceValidationSupportTest { @BeforeEach public void before() { myValueSetProvider = new MyValueSetProvider(); - myRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider); + ourRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider); myCodeSystemProvider = new MyCodeSystemProvider(); - myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider); + ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider); myConceptMapProvider = new MyConceptMapProvider(); - myRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider); + ourRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider); - String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort(); + String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx); mySvc.setBaseUrl(baseUrl); @@ -111,69 +115,98 @@ public class RemoteTerminologyServiceValidationSupportTest { } @Test - public void testValidateCode_SystemCodeDisplayUrl_BlankCode() { + public void testValidateCode_withBlankCode_returnsNull() { IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, "", DISPLAY, VALUE_SET_URL); - assertEquals(null, outcome); + assertNull(outcome); } - @Test - public void testLookupOperation_CodeSystem_Success() { - createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, - DISPLAY, null); - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE); + @Test + public void testLookupCode_forCodeSystemWithAllParams_returnsCorrectParameters() { + myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult(); + myCodeSystemProvider.myNextLookupCodeResult.setFound(true); + myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM); + myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE); + myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME); + myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY); + + // property + String propertyName = "birthDate"; + String propertyValue = "1930-01-01"; + IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue); + myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property); + + // designation + IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); + designation.setLanguage("en"); + designation.setUseCode("code"); + designation.setUseSystem("system"); + designation.setUseDisplay("display"); + designation.setValue("some value"); + myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation); + + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, null, Set.of("birthDate"))); assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!"); assertEquals(DISPLAY, outcome.getCodeDisplay()); - assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion()); + assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion()); + assertEquals(CODE_SYSTEM_NAME, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString()); assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString()); - assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameterValue("result").primitiveValue())); + + Parameters.ParametersParameterComponent propertyComponent = myCodeSystemProvider.myNextReturnParams.getParameter("property"); + assertNotNull(propertyComponent); + + Iterator propertyComponentIterator = propertyComponent.getPart().iterator(); + propertyComponent = propertyComponentIterator.next(); + assertEquals("code", propertyComponent.getName()); + assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue()); + + propertyComponent = propertyComponentIterator.next(); + assertEquals("value", propertyComponent.getName()); + assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue()); + + Parameters.ParametersParameterComponent designationComponent = myCodeSystemProvider.myNextReturnParams.getParameter("designation"); + Iterator partParameter = designationComponent.getPart().iterator(); + designationComponent = partParameter.next(); + assertEquals("language", designationComponent.getName()); + assertEquals(LANGUAGE, designationComponent.getValue().toString()); + + designationComponent = partParameter.next(); + assertEquals("use", designationComponent.getName()); + Coding coding = (Coding)designationComponent.getValue(); + assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!"); + assertEquals("code", coding.getCode()); + assertEquals("system", coding.getSystem()); + assertEquals("display", coding.getDisplay()); + + designationComponent = partParameter.next(); + assertEquals("value", designationComponent.getName()); + assertEquals("some value", designationComponent.getValue().toString()); } @Test - public void testLookupOperationWithAllParams_CodeSystem_Success() { - createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, - DISPLAY, null); - addAdditionalReturnParameters(); - - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE); - assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!"); - assertEquals(DISPLAY, outcome.getCodeDisplay()); - assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion()); - assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString()); - - assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); - assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - assertTrue(Boolean.parseBoolean(myCodeSystemProvider.myNextReturnParams.getParameterValue("result").primitiveValue())); - - validateExtraCodeSystemParams(); + public void testLookupCode_forCodeSystemWithBlankCode_throwsException() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""))); } @Test - public void testLookupCode_BlankCode_ThrowsException() { - Assertions.assertThrows(IllegalArgumentException.class, () -> { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, - "", null); - }); - } - - @Test - public void testValidateCode_ValueSet_Success() { + public void testValidateCode_forValueSet_returnsCorrectly() { createNextValueSetReturnParameters(true, DISPLAY, null); IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); assertEquals(CODE, myValueSetProvider.myLastCode.getCode()); assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue()); assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue()); assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue()); - assertEquals(null, myValueSetProvider.myLastValueSet); + assertNull(myValueSetProvider.myLastValueSet); } @Test @@ -182,75 +215,20 @@ public class RemoteTerminologyServiceValidationSupportTest { myValueSetProvider.myNextReturnValueSets = new ArrayList<>(); // when - IBaseResource valueSet = mySvc.fetchValueSet(VALUE_SET_URL); + mySvc.fetchValueSet(VALUE_SET_URL); // then assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam); } @Test - public void testValidateCodeWithAllParams_CodeSystem_Success() { - createNextCodeSystemReturnParameters(true, DISPLAY, null); - addAdditionalReturnParameters(); - - IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null); - assertEquals(CODE, outcome.getCode()); - assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); - - validateExtraCodeSystemParams(); - } - - private void validateExtraCodeSystemParams() { - assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); - assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) { - String paramName = param.getName(); - if (paramName.equals("result")) { - assertEquals(true, ((BooleanType)param.getValue()).booleanValue()); - } else if (paramName.equals("display")) { - assertEquals(DISPLAY, param.getValue().toString()); - } else if (paramName.equals("property")) { - for (Parameters.ParametersParameterComponent propertyComponent : param.getPart()) { - switch(propertyComponent.getName()) { - case "name": - assertEquals("birthDate", propertyComponent.getValue().toString()); - break; - case "value": - assertEquals("1930-01-01", propertyComponent.getValue().toString()); - break; - } - } - } else if (paramName.equals("designation")) { - for (Parameters.ParametersParameterComponent designationComponent : param.getPart()) { - switch(designationComponent.getName()) { - case "language": - assertEquals(LANGUAGE, designationComponent.getValue().toString()); - break; - case "use": - Coding coding = (Coding)designationComponent.getValue(); - assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!"); - assertEquals("code", coding.getCode()); - assertEquals("system", coding.getSystem()); - assertEquals("display", coding.getDisplay()); - break; - case "value": - assertEquals("some value", designationComponent.getValue().toString()); - break; - } - } - } - } - } - - @Test - public void testValidateCode_SystemCodeDisplayUrl_Error() { + public void testValidateCode_forSystemCodeWithError_returnsCorrectly() { createNextValueSetReturnParameters(false, null, ERROR_MESSAGE); IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL); - assertEquals(null, outcome.getCode()); - assertEquals(null, outcome.getDisplay()); + assertNotNull(outcome); + assertNull(outcome.getCode()); + assertNull(outcome.getDisplay()); assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity()); assertEquals(ERROR_MESSAGE, outcome.getMessage()); @@ -258,18 +236,24 @@ public class RemoteTerminologyServiceValidationSupportTest { assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue()); assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue()); assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue()); - assertEquals(null, myValueSetProvider.myLastValueSet); + assertNull(myValueSetProvider.myLastValueSet); } @Test - public void testValidateCodeInCodeSystem_Good() { - createNextCodeSystemReturnParameters(true, DISPLAY, null); + public void testValidateCode_forCodeSystem_returnsCorrectly() { + myCodeSystemProvider.myNextValidationResult = new IValidationSupport.CodeValidationResult(); + myCodeSystemProvider.myNextValidationResult.setCodeSystemVersion(CODE_SYSTEM); + myCodeSystemProvider.myNextValidationResult.setCode(CODE); + myCodeSystemProvider.myNextValidationResult.setCodeSystemName(CODE_SYSTEM_NAME); + myCodeSystemProvider.myNextValidationResult.setDisplay(DISPLAY); + myCodeSystemProvider.myNextValidationResult.setMessage(SUCCESS_MESSAGE); IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); @@ -284,16 +268,17 @@ public class RemoteTerminologyServiceValidationSupportTest { valueSet.setUrl(VALUE_SET_URL); IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); assertEquals(CODE, myValueSetProvider.myLastCode.getCode()); assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue()); assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue()); assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString()); - assertEquals(null, myValueSetProvider.myLastValueSet); + assertNull(myValueSetProvider.myLastValueSet); } /** @@ -307,7 +292,7 @@ public class RemoteTerminologyServiceValidationSupportTest { valueSet.setUrl(VALUE_SET_URL); IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet); - assertEquals(null, outcome); + assertNull(outcome); } @Test @@ -350,7 +335,8 @@ public class RemoteTerminologyServiceValidationSupportTest { TranslateConceptResults results = mySvc.translateConcept(request); - assertEquals(results.getResult(), true); + assertNotNull(results); + assertTrue(results.getResult()); assertEquals(results.getResults().size(), 2); for(TranslateConceptResult result : results.getResults()) { assertEquals(singleResult, result); @@ -374,8 +360,8 @@ public class RemoteTerminologyServiceValidationSupportTest { IValidationSupport.TranslateCodeRequest request = new IValidationSupport.TranslateCodeRequest(codings, null); TranslateConceptResults results = mySvc.translateConcept(request); - - assertEquals(results.getResult(), false); + assertNotNull(results); + assertFalse(results.getResult()); assertEquals(results.getResults().size(), 0); assertNull(myConceptMapProvider.myLastCodeableConcept); @@ -393,7 +379,7 @@ public class RemoteTerminologyServiceValidationSupportTest { myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>(); // when - IBaseResource codeSystem = mySvc.fetchCodeSystem("http://loinc.org"); + mySvc.fetchCodeSystem("http://loinc.org"); // then assertEquals(SummaryEnum.FALSE, myCodeSystemProvider.myLastSummaryParam); @@ -535,7 +521,7 @@ public class RemoteTerminologyServiceValidationSupportTest { /** * Captures the system parameter of the request */ - private class TestClientInterceptor implements IClientInterceptor { + private static class TestClientInterceptor implements IClientInterceptor { private String capturedSystemParameter; @@ -552,12 +538,12 @@ public class RemoteTerminologyServiceValidationSupportTest { capturedSystemParameter = systemValues.get(0); } } catch (IOException theE) { - theE.printStackTrace(); + // ignore } } @Override - public void interceptResponse(IHttpResponse theResponse) throws IOException { } + public void interceptResponse(IHttpResponse theResponse) { } public String getCapturedSystemParameter() { return capturedSystemParameter; } } @@ -570,7 +556,7 @@ public class RemoteTerminologyServiceValidationSupportTest { myValueSetProvider.myNextReturnValueSets = new ArrayList<>(); boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS"); - assertEquals(false, outcome); + assertFalse(outcome); assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue()); } @@ -580,7 +566,7 @@ public class RemoteTerminologyServiceValidationSupportTest { myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123")); boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS"); - assertEquals(true, outcome); + assertTrue(outcome); assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue()); } @@ -589,7 +575,7 @@ public class RemoteTerminologyServiceValidationSupportTest { myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>(); boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org"); - assertEquals(false, outcome); + assertFalse(outcome); assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue()); } @@ -599,31 +585,10 @@ public class RemoteTerminologyServiceValidationSupportTest { myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/123")); boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org"); - assertEquals(true, outcome); + assertTrue(outcome); assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue()); } - private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) { - myCodeSystemProvider.myNextReturnParams = new Parameters(); - myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult); - myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay); - if (theMessage != null) { - myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage); - } - } - - private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText, - String theDisplay, String theMessage) { - myCodeSystemProvider.myNextReturnParams = new Parameters(); - myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult); - myCodeSystemProvider.myNextReturnParams.addParameter("version", theVersion); - myCodeSystemProvider.myNextReturnParams.addParameter("name", theVersionAsText); - myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay); - if (theMessage != null) { - myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage); - } - } - private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) { myValueSetProvider.myNextReturnParams = new Parameters(); myValueSetProvider.myNextReturnParams.addParameter("result", theResult); @@ -633,53 +598,33 @@ public class RemoteTerminologyServiceValidationSupportTest { } } - private void addAdditionalReturnParameters() { - // property - Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property"); - param.addPart().setName("name").setValue(new StringType("birthDate")); - param.addPart().setName("value").setValue(new StringType("1930-01-01")); - // designation - param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation"); - param.addPart().setName("language").setValue(new CodeType("en")); - Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use"); - Coding coding = new Coding(); - coding.setCode("code"); - coding.setSystem("system"); - coding.setDisplay("display"); - codingParam.setValue(coding); - param.addPart().setName("value").setValue(new StringType("some value")); - } - private static class MyCodeSystemProvider implements IResourceProvider { private SummaryEnum myLastSummaryParam; private UriParam myLastUrlParam; private List myNextReturnCodeSystems; - private int myInvocationCount; private UriType myLastUrl; private CodeType myLastCode; - private Coding myLastCoding; - private StringType myLastVersion; private Parameters myNextReturnParams; private IValidationSupport.LookupCodeResult myNextLookupCodeResult; + private IValidationSupport.CodeValidationResult myNextValidationResult; @Operation(name = "validate-code", idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1), @OperationParam(name = "message", type = StringType.class), @OperationParam(name = "display", type = StringType.class) }) - public Parameters validateCode( + public IBaseParameters validateCode( HttpServletRequest theServletRequest, @IdParam(optional = true) IdType theId, @OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl, @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay ) { - myInvocationCount++; myLastUrl = theCodeSystemUrl; myLastCode = theCode; + myNextReturnParams = (Parameters)myNextValidationResult.toParameters(ourCtx); return myNextReturnParams; - } @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { @@ -687,22 +632,21 @@ public class RemoteTerminologyServiceValidationSupportTest { @OperationParam(name="version", type=StringType.class, min=0), @OperationParam(name="display", type=StringType.class, min=1), @OperationParam(name="abstract", type=BooleanType.class, min=1), + @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) }) - public Parameters lookup( + public IBaseParameters lookup( HttpServletRequest theServletRequest, @OperationParam(name="code", min=0, max=1) CodeType theCode, @OperationParam(name="system", min=0, max=1) UriType theSystem, @OperationParam(name="coding", min=0, max=1) Coding theCoding, @OperationParam(name="version", min=0, max=1) StringType theVersion, @OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage, - @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, + @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List thePropertyNames, RequestDetails theRequestDetails ) { - myInvocationCount++; myLastCode = theCode; myLastUrl = theSystem; - myLastCoding = theCoding; - myLastVersion = theVersion; + myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); return myNextReturnParams; } @@ -779,8 +723,6 @@ public class RemoteTerminologyServiceValidationSupportTest { private UriType myLastTargetValueSet; private UriType myLastTargetCodeSystem; private BooleanType myLastReverse; - - private int myInvocationCount; private Parameters myNextReturnParams; @Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = { @@ -799,7 +741,6 @@ public class RemoteTerminologyServiceValidationSupportTest { @OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse, RequestDetails theRequestDetails ) { - myInvocationCount++; myLastConceptMapUrl = theConceptMapUrl; myLastConceptMapVersion = theConceptMapVersion; myLastCodeableConcept = theSourceCodeableConcept; diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java index 9d435406dd8..017eea69f19 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java @@ -2,6 +2,7 @@ package org.hl7.fhir.dstu3.hapi.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -9,13 +10,11 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; -import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.DateType; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.UriType; @@ -25,7 +24,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import javax.servlet.http.HttpServletRequest; +import java.util.Iterator; import java.util.List; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -34,14 +35,13 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test { private static final String DISPLAY = "DISPLAY"; private static final String LANGUAGE = "en"; private static final String CODE_SYSTEM = "CODE_SYS"; - private static final String CODE_SYSTEM_VERSION = "2.1"; - private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12"; + private static final String CODE_SYSTEM_NAME = "Code System"; private static final String CODE = "CODE"; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3(); @RegisterExtension - public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx); + public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx); private RemoteTerminologyServiceValidationSupport mySvc; private MyCodeSystemProvider myCodeSystemProvider; @@ -49,143 +49,98 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test { @BeforeEach public void before() { myCodeSystemProvider = new MyCodeSystemProvider(); - myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider); - String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort(); + ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider); + String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx); mySvc.setBaseUrl(baseUrl); mySvc.addClientInterceptor(new LoggingInterceptor(true)); } - @Test - public void testLookupOperation_CodeSystem_Success() { - createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY); - - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE); - assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!"); - assertEquals(DISPLAY, outcome.getCodeDisplay()); - assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion()); - - assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue()); - assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) { - String paramName = param.getName(); - if (paramName.equals("result")) { - assertEquals(true, ((BooleanType)param.getValue()).booleanValue()); - } else if (paramName.equals("version")) { - assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString()); - } else if (paramName.equals("display")) { - assertEquals(DISPLAY, param.getValue().toString()); - } else if (paramName.equals("name")) { - assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString()); - } - } - } - @Test public void testLookupOperationWithAllParams_CodeSystem_Success() { - createNextCodeSystemLookupReturnParameters(true, CODE_SYSTEM_VERSION, CODE_SYSTEM_VERSION_AS_TEXT, DISPLAY, LANGUAGE); - addAdditionalCodeSystemLookupReturnParameters(); + myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult(); + myCodeSystemProvider.myNextLookupCodeResult.setFound(true); + myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM); + myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE); + myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME); + myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY); - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, CODE_SYSTEM, CODE, LANGUAGE); + // property + String propertyName = "birthDate"; + String propertyValue = "1930-01-01"; + IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue); + myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property); + + // designation + IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); + designation.setLanguage("en"); + designation.setUseCode("code"); + designation.setUseSystem("system"); + designation.setUseDisplay("display"); + designation.setValue("some value"); + myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation); + + IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, Set.of("birthDate"))); assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!"); assertEquals(DISPLAY, outcome.getCodeDisplay()); - assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion()); + assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion()); assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue()); assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - for (Parameters.ParametersParameterComponent param : myCodeSystemProvider.myNextReturnParams.getParameter()) { - String paramName = param.getName(); - if (paramName.equals("result")) { - assertEquals(true, ((BooleanType)param.getValue()).booleanValue()); - } else if (paramName.equals("version")) { - assertEquals(CODE_SYSTEM_VERSION, param.getValue().toString()); - } else if (paramName.equals("display")) { - assertEquals(DISPLAY, param.getValue().toString()); - } else if (paramName.equals("name")) { - assertEquals(CODE_SYSTEM_VERSION_AS_TEXT, param.getValue().toString()); - } else if (paramName.equals("language")) { - assertEquals(LANGUAGE, param.getValue().toString()); - } else if (paramName.equals("property")) { - for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent propertyComponent : param.getPart()) { - switch(propertyComponent.getName()) { - case "name": - assertEquals("birthDate", propertyComponent.getValue().toString()); - break; - case "value": - assertEquals("1930-01-01", ((DateType)propertyComponent.getValue()).asStringValue()); - break; - } - } - } else if (paramName.equals("designation")) { - for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent : param.getPart()) { - switch(designationComponent.getName()) { - case "language": - assertEquals(LANGUAGE, designationComponent.getValue().toString()); - break; - case "use": - Coding coding = (Coding)designationComponent.getValue(); - assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!"); - assertEquals("code", coding.getCode()); - assertEquals("system", coding.getSystem()); - assertEquals("display", coding.getDisplay()); - break; - case "value": - assertEquals("some value", designationComponent.getValue().toString()); - break; - } - } + + Parameters.ParametersParameterComponent propertyComponent = null; + Parameters.ParametersParameterComponent designationComponent = null; + for (Parameters.ParametersParameterComponent parameterComponent : myCodeSystemProvider.myNextReturnParams.getParameter()) { + if ("property".equals(parameterComponent.getName())) { + propertyComponent = parameterComponent; + } + if ("designation".equals(parameterComponent.getName())) { + designationComponent = parameterComponent; } } - } - private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText, - String theDisplay) { - createNextCodeSystemLookupReturnParameters(theResult, theVersion, theVersionAsText, theDisplay, null); - } + assertNotNull(propertyComponent); - private void createNextCodeSystemLookupReturnParameters(boolean theResult, String theVersion, String theVersionAsText, - String theDisplay, String theLanguage) { - myCodeSystemProvider.myNextReturnParams = new Parameters(); - myCodeSystemProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(theResult)); - myCodeSystemProvider.myNextReturnParams.addParameter().setName("version").setValue(new StringType(theVersion)); - myCodeSystemProvider.myNextReturnParams.addParameter().setName("name").setValue(new StringType(theVersionAsText)); - myCodeSystemProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(theDisplay)); - if (!StringUtils.isBlank(theLanguage)) { - myCodeSystemProvider.myNextReturnParams.addParameter().setName("language").setValue(new StringType(theLanguage)); - } - } + Iterator propertyComponentIterator = propertyComponent.getPart().iterator(); + propertyComponent = propertyComponentIterator.next(); + assertEquals("code", propertyComponent.getName()); + assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue()); - private void addAdditionalCodeSystemLookupReturnParameters() { - // property - Parameters.ParametersParameterComponent param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("property"); - param.addPart().setName("name").setValue(new StringType("birthDate")); - param.addPart().setName("value").setValue(new DateType("1930-01-01")); - // designation - param = myCodeSystemProvider.myNextReturnParams.addParameter().setName("designation"); - param.addPart().setName("language").setValue(new CodeType("en")); - Parameters.ParametersParameterComponent codingParam = param.addPart().setName("use"); - Coding coding = new Coding(); - coding.setCode("code"); - coding.setSystem("system"); - coding.setDisplay("display"); - codingParam.setValue(coding); - param.addPart().setName("value").setValue(new StringType("some value")); + propertyComponent = propertyComponentIterator.next(); + assertEquals("value", propertyComponent.getName()); + assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue()); + + assertNotNull(designationComponent); + Iterator partParameter = designationComponent.getPart().iterator(); + designationComponent = partParameter.next(); + assertEquals("language", designationComponent.getName()); + assertEquals(LANGUAGE, designationComponent.getValue().toString()); + + designationComponent = partParameter.next(); + assertEquals("use", designationComponent.getName()); + Coding coding = (Coding)designationComponent.getValue(); + assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!"); + assertEquals("code", coding.getCode()); + assertEquals("system", coding.getSystem()); + assertEquals("display", coding.getDisplay()); + + designationComponent = partParameter.next(); + assertEquals("value", designationComponent.getName()); + assertEquals("some value", designationComponent.getValue().toString()); } private static class MyCodeSystemProvider implements IResourceProvider { - private int myInvocationCount; private UriType myLastUrl; private CodeType myLastCode; - private Coding myLastCoding; - private StringType myLastVersion; - private CodeType myLastDisplayLanguage; private Parameters myNextReturnParams; + private IValidationSupport.LookupCodeResult myNextLookupCodeResult; @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= { @OperationParam(name="name", type=StringType.class, min=1), @OperationParam(name="version", type=StringType.class, min=0), @OperationParam(name="display", type=StringType.class, min=1), @OperationParam(name="abstract", type=BooleanType.class, min=1), + @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) }) public Parameters lookup( HttpServletRequest theServletRequest, @@ -194,15 +149,13 @@ public class RemoteTerminologyServiceValidationSupportDstu3Test { @OperationParam(name="coding", min=0, max=1) Coding theCoding, @OperationParam(name="version", min=0, max=1) StringType theVersion, @OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage, - @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, + @OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List thePropertyNames, RequestDetails theRequestDetails ) { - myInvocationCount++; myLastCode = theCode; myLastUrl = theSystem; - myLastCoding = theCoding; - myLastVersion = theVersion; - myLastDisplayLanguage = theDisplayLanguage; + + myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); return myNextReturnParams; } diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index 4484737c1c8..7f29442a01a 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.rest.api.Constants; @@ -264,9 +265,10 @@ public class FhirInstanceValidatorR4Test extends BaseTest { return retVal; } }); - when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { - String system = t.getArgument(1, String.class); - String code = t.getArgument(2, String.class); + when(myMockSupport.lookupCode(any(), any())).thenAnswer(t -> { + LookupCodeRequest request = t.getArgument(1, LookupCodeRequest.class); + String system = request.getSystem(); + String code = request.getCode(); if (myValidConcepts.contains(system + "___" + code)) { return new IValidationSupport.LookupCodeResult().setFound(true); } else { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java index 97f735b9ebb..f700c6e6744 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4b/validation/FhirInstanceValidatorR4BTest.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.rest.api.Constants; @@ -31,9 +32,33 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4b.conformance.ProfileUtilities; import org.hl7.fhir.r4b.context.IWorkerContext; import org.hl7.fhir.r4b.hapi.ctx.HapiWorkerContext; -import org.hl7.fhir.r4b.model.*; +import org.hl7.fhir.r4b.model.AllergyIntolerance; +import org.hl7.fhir.r4b.model.Base; +import org.hl7.fhir.r4b.model.Base64BinaryType; +import org.hl7.fhir.r4b.model.BooleanType; +import org.hl7.fhir.r4b.model.Bundle; import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4b.model.CodeSystem; +import org.hl7.fhir.r4b.model.CodeType; +import org.hl7.fhir.r4b.model.Consent; +import org.hl7.fhir.r4b.model.ContactPoint; +import org.hl7.fhir.r4b.model.DateTimeType; +import org.hl7.fhir.r4b.model.Enumerations; +import org.hl7.fhir.r4b.model.Extension; +import org.hl7.fhir.r4b.model.Media; +import org.hl7.fhir.r4b.model.Narrative; +import org.hl7.fhir.r4b.model.Observation; +import org.hl7.fhir.r4b.model.OperationOutcome; +import org.hl7.fhir.r4b.model.Patient; +import org.hl7.fhir.r4b.model.Period; +import org.hl7.fhir.r4b.model.Practitioner; +import org.hl7.fhir.r4b.model.Procedure; +import org.hl7.fhir.r4b.model.QuestionnaireResponse; +import org.hl7.fhir.r4b.model.Reference; +import org.hl7.fhir.r4b.model.StringType; +import org.hl7.fhir.r4b.model.StructureDefinition; import org.hl7.fhir.r4b.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r4b.model.ValueSet; import org.hl7.fhir.r4b.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r4b.terminologies.ValueSetExpander; import org.hl7.fhir.r4b.utils.FHIRPathEngine; @@ -72,7 +97,6 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -230,9 +254,10 @@ public class FhirInstanceValidatorR4BTest extends BaseTest { return retVal; } }); - when(myMockSupport.lookupCode(any(), any(), any(), any())).thenAnswer(t -> { - String system = t.getArgument(1, String.class); - String code = t.getArgument(2, String.class); + when(myMockSupport.lookupCode(any(), any())).thenAnswer(t -> { + LookupCodeRequest request = t.getArgument(1, LookupCodeRequest.class); + String system = request.getSystem(); + String code = request.getCode(); if (myValidConcepts.contains(system + "___" + code)) { return new IValidationSupport.LookupCodeResult().setFound(true); } else { diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java index 2d5c71bce56..0e9485bae4d 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r5/validation/RemoteTerminologyServiceValidationSupportR5Test.java @@ -1,8 +1,8 @@ package org.hl7.fhir.r5.validation; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.junit.jupiter.api.Assertions; @@ -12,9 +12,9 @@ import org.junit.jupiter.api.extension.RegisterExtension; public class RemoteTerminologyServiceValidationSupportR5Test { private static final String ANY_NONBLANK_VALUE = "anything"; - private static FhirContext ourCtx = FhirContext.forR5Cached(); + private static final FhirContext ourCtx = FhirContext.forR5Cached(); @RegisterExtension - public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx); + public static RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx); private RemoteTerminologyServiceValidationSupport mySvc; @@ -27,9 +27,9 @@ public class RemoteTerminologyServiceValidationSupportR5Test { @Test public void testLookupCode_R5_ThrowsException() { - Assertions.assertThrows(UnsupportedOperationException.class, () -> { - IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode( - new ValidationSupportContext(ourCtx.getValidationSupport()), ANY_NONBLANK_VALUE, ANY_NONBLANK_VALUE); - }); + Assertions.assertThrows(UnsupportedOperationException.class, + () -> mySvc.lookupCode( + new ValidationSupportContext(ourCtx.getValidationSupport()), + new LookupCodeRequest(ANY_NONBLANK_VALUE, ANY_NONBLANK_VALUE))); } } From 6e683405a1840cbfc4d0006f4ab81719e3f83c88 Mon Sep 17 00:00:00 2001 From: TipzCM Date: Tue, 28 Nov 2023 16:28:17 -0500 Subject: [PATCH 49/86] 5237 fixing empty last page if even results (#5506) paging will not return empty pages if no results left --- .../ca/uhn/fhir/cli/BulkImportCommandIT.java | 11 +- .../5192-include-in-search-paging-fix.yaml | 7 ++ .../search/PersistedJpaBundleProvider.java | 20 +++- ...istedJpaSearchFirstPageBundleProvider.java | 16 ++- .../jpa/search/SynchronousSearchSvcImpl.java | 25 +++- .../search/SynchronousSearchSvcImplTest.java | 16 ++- .../jpa/dao/r4/ConsentEventsDaoR4Test.java | 38 ++++-- .../r4/FhirResourceDaoR4QueryCountTest.java | 23 +--- .../ForceOffsetSearchModeInterceptorTest.java | 43 +------ ...sourceProviderCustomSearchParamR4Test.java | 7 +- .../r4/ResourceProviderR4EverythingTest.java | 1 - .../provider/r4/ResourceProviderR4Test.java | 100 +++++++++++++++- .../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 5 +- .../jpa/test/config/TestHapiJpaConfig.java | 21 ++++ .../fhir/jpa/test/config/TestR4Config.java | 3 +- .../fhir/rest/api/server/IBundleProvider.java | 1 + .../rest/server/SimpleBundleProvider.java | 14 +++ .../server/method/ResponseBundleBuilder.java | 8 +- .../fhir/rest/server/method/ResponsePage.java | 111 +++++++++++++++--- .../api/server/method/ResponsePageTest.java | 59 +++++++--- .../jobs/imprt/BulkDataImportProvider.java | 6 +- 21 files changed, 403 insertions(+), 132 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java index 02d3e24325b..c7f4369faef 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java @@ -8,6 +8,8 @@ import ca.uhn.fhir.batch2.model.JobInstanceStartRequest; import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; import ca.uhn.fhir.system.HapiSystemProperties; @@ -66,6 +68,8 @@ public class BulkImportCommandIT { private IJobCoordinator myJobCoordinator; private final BulkDataImportProvider myProvider = new BulkDataImportProvider(); private final FhirContext myCtx = FhirContext.forR4Cached(); + @Mock + private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; @RegisterExtension public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx, myProvider) .registerInterceptor(new LoggingInterceptor()); @@ -77,6 +81,7 @@ public class BulkImportCommandIT { public void beforeEach() throws IOException { myProvider.setFhirContext(myCtx); myProvider.setJobCoordinator(myJobCoordinator); + myProvider.setRequestPartitionHelperService(myRequestPartitionHelperSvc); myTempDir = Files.createTempDirectory("hapifhir"); ourLog.info("Created temp directory: {}", myTempDir); } @@ -123,7 +128,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); JobInstanceStartRequest startRequest = myStartCaptor.getValue(); BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class); @@ -165,7 +170,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); JobInstanceStartRequest startRequest = myStartCaptor.getValue(); BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class); @@ -206,7 +211,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); try{ JobInstanceStartRequest startRequest = myStartCaptor.getValue(); diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml new file mode 100644 index 00000000000..a63588d0a9f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5192 +title: "Fixed a bug where search Bundles with `include` entries from an _include query parameter might + trigger a 'next' link to blank pages when + no more results `match` results are available. +" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index 5d2dae49723..51a994e7afa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -125,7 +125,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider { * of this class, since it's a prototype */ private Search mySearchEntity; - private String myUuid; + private final String myUuid; private SearchCacheStatusEnum myCacheStatus; private RequestPartitionId myRequestPartitionId; @@ -259,13 +259,21 @@ public class PersistedJpaBundleProvider implements IBundleProvider { final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(dao, resourceName, resourceType); RequestPartitionId requestPartitionId = getRequestPartitionId(); - final List pidsSubList = - mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex, myRequest, requestPartitionId); + // we request 1 more resource than we need + // this is so we can be sure of when we hit the last page + // (when doing offset searches) + final List pidsSubList = mySearchCoordinatorSvc.getResources( + myUuid, theFromIndex, theToIndex + 1, myRequest, requestPartitionId); + // max list size should be either the entire list, or from - to length + int maxSize = Math.min(theToIndex - theFromIndex, pidsSubList.size()); + theResponsePageBuilder.setTotalRequestedResourcesFetched(pidsSubList.size()); + + List firstBatchOfPids = pidsSubList.subList(0, maxSize); List resources = myTxService .withRequest(myRequest) .withRequestPartitionId(requestPartitionId) .execute(() -> { - return toResourceList(sb, pidsSubList, theResponsePageBuilder); + return toResourceList(sb, firstBatchOfPids, theResponsePageBuilder); }); return resources; @@ -541,8 +549,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider { // this can (potentially) change the results being returned. int precount = resources.size(); resources = ServerInterceptorUtil.fireStoragePreshowResource(resources, myRequest, myInterceptorBroadcaster); - // we only care about omitted results from *this* page - theResponsePageBuilder.setToOmittedResourceCount(precount - resources.size()); + // we only care about omitted results from this page + theResponsePageBuilder.setOmittedResourceCount(precount - resources.size()); theResponsePageBuilder.setResources(resources); theResponsePageBuilder.setIncludedResourceCount(includedPidList.size()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java index b1ac13fcbe2..f2357932a0e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java @@ -73,16 +73,23 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl mySearchTask.awaitInitialSync(); + // request 1 more than we need to, in order to know if there are extra values ourLog.trace("Fetching search resource PIDs from task: {}", mySearchTask.getClass()); - final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex); + final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex + 1); ourLog.trace("Done fetching search resource PIDs"); + int countOfPids = pids.size(); + ; + int maxSize = Math.min(theToIndex - theFromIndex, countOfPids); + thePageBuilder.setTotalRequestedResourcesFetched(countOfPids); + RequestPartitionId requestPartitionId = getRequestPartitionId(); + List firstBatch = pids.subList(0, maxSize); List retVal = myTxService .withRequest(myRequest) .withRequestPartitionId(requestPartitionId) - .execute(() -> toResourceList(mySearchBuilder, pids, thePageBuilder)); + .execute(() -> toResourceList(mySearchBuilder, firstBatch, thePageBuilder)); long totalCountWanted = theToIndex - theFromIndex; long totalCountMatch = (int) retVal.stream().filter(t -> !isInclude(t)).count(); @@ -103,12 +110,15 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl long remainingWanted = totalCountWanted - totalCountMatch; long fromIndex = theToIndex - remainingWanted; - List remaining = super.getResources((int) fromIndex, theToIndex, thePageBuilder); + ResponsePage.ResponsePageBuilder pageBuilder = new ResponsePage.ResponsePageBuilder(); + pageBuilder.setBundleProvider(this); + List remaining = super.getResources((int) fromIndex, theToIndex, pageBuilder); remaining.forEach(t -> { if (!existingIds.contains(t.getIdElement().getValue())) { retVal.add(t); } }); + thePageBuilder.combineWith(pageBuilder); } } ourLog.trace("Loaded resources to return"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java index 3627b72a5d4..89f77e51b14 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java @@ -115,7 +115,7 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { .execute(() -> { // Load the results synchronously - final List pids = new ArrayList<>(); + List pids = new ArrayList<>(); Long count = 0L; if (wantCount) { @@ -145,8 +145,17 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { return bundleProvider; } + // if we have a count, we'll want to request + // additional resources + SearchParameterMap clonedParams = theParams.clone(); + Integer requestedCount = clonedParams.getCount(); + boolean hasACount = requestedCount != null; + if (hasACount) { + clonedParams.setCount(requestedCount.intValue() + 1); + } + try (IResultIterator resultIter = theSb.createQuery( - theParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) { + clonedParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) { while (resultIter.hasNext()) { pids.add(resultIter.next()); if (theLoadSynchronousUpTo != null && pids.size() >= theLoadSynchronousUpTo) { @@ -162,6 +171,15 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { throw new InternalErrorException(Msg.code(1164) + e); } + // truncate the list we retrieved - if needed + int receivedResourceCount = -1; + if (hasACount) { + // we want the accurate received resource count + receivedResourceCount = pids.size(); + int resourcesToReturn = Math.min(theParams.getCount(), pids.size()); + pids = pids.subList(0, resourcesToReturn); + } + JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(pids, () -> theSb); HookParams params = new HookParams() .add(IPreResourceAccessDetails.class, accessDetails) @@ -228,6 +246,9 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { resources, theRequestDetails, myInterceptorBroadcaster); SimpleBundleProvider bundleProvider = new SimpleBundleProvider(resources); + if (hasACount) { + bundleProvider.setTotalResourcesRequestedReturned(receivedResourceCount); + } if (theParams.isOffsetQuery()) { bundleProvider.setCurrentPageOffset(theParams.getOffset()); bundleProvider.setCurrentPageSize(theParams.getCount()); diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java index 5c9309b4369..456f42ee90a 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java @@ -42,14 +42,17 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { @Test public void testSynchronousSearch() { - when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); + when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())) + .thenReturn(mySearchBuilder); SearchParameterMap params = new SearchParameterMap(); List pids = createPidSequence(800); - when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), any(), nullable(RequestPartitionId.class))) + .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); - doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); + doAnswer(loadPids()).when(mySearchBuilder) + .loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); IBundleProvider result = mySynchronousSearchSvc.executeQuery( "Patient", params, RequestPartitionId.allPartitions()); assertNull(result.getUuid()); @@ -71,8 +74,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); List pids = createPidSequence(30); - when(mySearchBuilder.createCountQuery(same(params), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L); - when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator())); + when(mySearchBuilder.createCountQuery(any(SearchParameterMap.class), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator())); doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); @@ -92,7 +95,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { params.setLoadSynchronousUpTo(100); List pids = createPidSequence(800); - when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))) + .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); pids = createPidSequence(110); List finalPids = pids; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java index 0c1e49496f6..b5493dcebd5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java @@ -29,11 +29,11 @@ import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.servlet.ServletException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.leftPad; @@ -54,7 +54,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { private List myPatientIds; private List myObservationIdsOddOnly; private List myObservationIdsEvenOnly; - private List myObservationIdsWithVersions; + private List myObservationIdsWithoutVersions; private List myPatientIdsEvenOnly; @AfterEach @@ -64,13 +64,16 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { } @BeforeEach - public void before() throws ServletException { + @Override + public void beforeInitMocks() throws Exception { + super.beforeInitMocks(); RestfulServer restfulServer = new RestfulServer(); restfulServer.setPagingProvider(myPagingProvider); when(mySrd.getServer()).thenReturn(restfulServer); myStorageSettings.setSearchPreFetchThresholds(Arrays.asList(20, 50, 190)); + restfulServer.setDefaultPageSize(null); } @Test @@ -147,6 +150,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { @Test public void testSearchAndBlockSome_LoadSynchronous() { + // setup create50Observations(); AtomicInteger hitCount = new AtomicInteger(0); @@ -281,6 +285,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { @Test public void testSearchAndBlockSomeOnIncludes_LoadSynchronous() { + // setup create50Observations(); AtomicInteger hitCount = new AtomicInteger(0); @@ -328,9 +333,8 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { * returned results because we create it then update it in create50Observations() */ assertEquals(1, hitCount.get()); - assertEquals(myObservationIdsWithVersions.subList(90, myObservationIdsWithVersions.size()), sort(interceptedResourceIds)); + assertEquals(sort(myObservationIdsWithoutVersions.subList(90, myObservationIdsWithoutVersions.size())), sort(interceptedResourceIds)); returnedIdValues.forEach(t -> assertTrue(new IdType(t).getIdPartAsLong() % 2 == 0)); - } @Test @@ -363,7 +367,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { private void create50Observations() { myPatientIds = new ArrayList<>(); myObservationIds = new ArrayList<>(); - myObservationIdsWithVersions = new ArrayList<>(); + myObservationIdsWithoutVersions = new ArrayList<>(); Patient p = new Patient(); p.setActive(true); @@ -383,9 +387,9 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { final Observation obs1 = new Observation(); obs1.setStatus(Observation.ObservationStatus.FINAL); obs1.addIdentifier().setSystem("urn:system").setValue("I" + leftPad("" + i, 5, '0')); - IIdType obs1id = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); + IIdType obs1id = myObservationDao.create(obs1).getId(); myObservationIds.add(obs1id.toUnqualifiedVersionless().getValue()); - myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue()); + myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue()); obs1.setId(obs1id); if (obs1id.getIdPartAsLong() % 2 == 0) { @@ -394,7 +398,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { obs1.getSubject().setReference(oddPid); } myObservationDao.update(obs1); - myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue()); + myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue()); } @@ -483,14 +487,24 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { } } - private static List sort(List... theLists) { + private List sort(List... theLists) { + return sort(id -> { + String idParsed = id.substring(id.indexOf("/") + 1); + if (idParsed.contains("/_history")) { + idParsed = idParsed.substring(0, idParsed.indexOf("/")); + } + return Long.parseLong(idParsed); + }, theLists); + } + + private List sort(Function theParser, List... theLists) { ArrayList retVal = new ArrayList<>(); for (List next : theLists) { retVal.addAll(next); } retVal.sort((o0, o1) -> { - long i0 = Long.parseLong(o0.substring(o0.indexOf('/') + 1)); - long i1 = Long.parseLong(o1.substring(o1.indexOf('/') + 1)); + long i0 = theParser.apply(o0); + long i1 = theParser.apply(o1); return (int) (i0 - i1); }); return retVal; 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 e1a535b678b..457ba5e943d 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 @@ -114,6 +114,7 @@ import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.eq; @@ -1229,6 +1230,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test nextChunk.forEach(t -> foundIds.add(t.getIdElement().toUnqualifiedVersionless().getValue())); } + assertEquals(ids.size(), foundIds.size()); ids.sort(new ComparableComparator<>()); foundIds.sort(new ComparableComparator<>()); assertEquals(ids, foundIds); @@ -1327,7 +1329,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -1343,7 +1345,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -1351,22 +1353,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(1, myCaptureQueriesListener.countCommits()); assertEquals(0, myCaptureQueriesListener.countRollbacks()); - assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true")); - - // Third page (no results) - - myCaptureQueriesListener.clear(); - outcome = myClient.search().forResource("Patient").where(Patient.ACTIVE.exactly().code("true")).offset(10).count(5).returnBundle(Bundle.class).execute(); - assertThat(toUnqualifiedVersionlessIdValues(outcome).toString(), toUnqualifiedVersionlessIdValues(outcome), empty()); - myCaptureQueriesListener.logSelectQueries(); - assertEquals(1, myCaptureQueriesListener.countSelectQueries()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'")); - assertEquals(0, myCaptureQueriesListener.countInsertQueries()); - assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); - + assertNull(outcome.getLink("next")); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java index 716d631232b..945f6f19472 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java @@ -13,10 +13,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -66,7 +63,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -91,7 +88,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -99,31 +96,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 assertEquals(1, myCaptureQueriesListener.countCommits()); assertEquals(0, myCaptureQueriesListener.countRollbacks()); - assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true")); - - // Third page (no results) - - myCaptureQueriesListener.clear(); - Bundle outcome3 = myClient - .search() - .forResource("Patient") - .where(Patient.ACTIVE.exactly().code("true")) - .offset(10) - .count(5) - .returnBundle(Bundle.class) - .execute(); - assertThat(toUnqualifiedVersionlessIdValues(outcome3).toString(), toUnqualifiedVersionlessIdValues(outcome3), empty()); - myCaptureQueriesListener.logSelectQueries(); - assertEquals(1, myCaptureQueriesListener.countSelectQueries()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'")); - assertEquals(0, myCaptureQueriesListener.countInsertQueries()); - assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); - - assertNull(outcome3.getLink("next"), () -> outcome3.getLink("next").getUrl()); - + assertNull(outcome.getLink("next")); } @Test @@ -148,11 +121,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 assertThat(secondPageBundle.getEntry(), hasSize(5)); - Bundle thirdPageBundle = myClient.loadPage().next(secondPageBundle).execute(); - - assertThat(thirdPageBundle.getEntry(), hasSize(0)); - assertNull(thirdPageBundle.getLink("next"), () -> thirdPageBundle.getLink("next").getUrl()); - + assertNull(secondPageBundle.getLink("next")); } @@ -180,7 +149,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -203,7 +172,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java index 22f2e3e19df..d5ac558d272 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java @@ -484,7 +484,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide */ @Test public void testCustomParameterMatchingManyValues() { - List found = new ArrayList<>(); class Interceptor { @@ -496,7 +495,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide Interceptor interceptor = new Interceptor(); myInterceptorRegistry.registerInterceptor(interceptor); try { - int textIndex = 0; List ids = new ArrayList<>(); for (int i = 0; i < 200; i++) { @@ -549,9 +547,8 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide ourLog.info("Found: {}", found); runInTransaction(() -> { - - List currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); - List currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList(); + List currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); + List currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList(); List searches = mySearchEntityDao.findAll(); assertEquals(1, searches.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java index ea08b3b0b15..171120f54cb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java @@ -1012,7 +1012,6 @@ public class ResourceProviderR4EverythingTest extends BaseResourceProviderR4Test assertThat(ids, containsInAnyOrder("Patient/FOO", "Observation/BAZ")); } - @Test public void testPagingOverEverythingSet() throws InterruptedException { Patient p = new Patient(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index f2cb94da312..722cc8ae8e1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -25,11 +25,13 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchTotalModeEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.client.api.IClientInterceptor; @@ -42,6 +44,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; @@ -159,8 +162,10 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Spy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.TransactionStatus; @@ -220,6 +225,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; @SuppressWarnings("Duplicates") public class ResourceProviderR4Test extends BaseResourceProviderR4Test { @@ -255,6 +263,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myStorageSettings.setUpdateWithHistoryRewriteEnabled(false); myStorageSettings.setPreserveRequestIdInResourceBody(false); + when(myPagingProvider.canStoreSearchResults()) + .thenCallRealMethod(); } @BeforeEach @@ -2718,6 +2728,90 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { assertEquals(total + 1, ids.size()); } + + @ParameterizedTest + @CsvSource({ + "true,19,10", + "false,19,10", + "true,20,0", + "false,20,0" + }) + public void testPagingWithIncludesReturnsConsistentValues( + boolean theAllowStoringSearchResults, + int theResourceCount, + int theOrgCount + ) { + // setup + + // create resources + { + Coding tagCode = new Coding(); + tagCode.setCode("test"); + tagCode.setSystem("http://example.com"); + int orgCount = theOrgCount; + for (int i = 0; i < theResourceCount; i++) { + Task t = new Task(); + t.getMeta() + .addTag(tagCode); + t.setStatus(Task.TaskStatus.REQUESTED); + if (orgCount > 0) { + Organization org = new Organization(); + org.setName("ORG"); + IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + + orgCount--; + t.getOwner().setReference(orgId.getValue()); + } + myTaskDao.create(t); + } + } + + // when + if (!theAllowStoringSearchResults) { + // we don't actually allow this in our current + // pagingProvider implementations (except for history). + // But we will test with it because our ResponsePage + // is what's under test here + when(myPagingProvider.canStoreSearchResults()) + .thenReturn(false); + } + + int requestedAmount = 10; + Bundle bundle = myClient + .search() + .byUrl("Task?_count=10&_tag=test&status=requested&_include=Task%3Aowner&_sort=status") + .returnBundle(Bundle.class) + .execute(); + int count = bundle.getEntry().size(); + assertFalse(bundle.getEntry().isEmpty()); + + String nextUrl = null; + do { + Bundle.BundleLinkComponent nextLink = bundle.getLink("next"); + if (nextLink != null) { + nextUrl = nextLink.getUrl(); + + // make sure we're always requesting 10 + assertTrue(nextUrl.contains(String.format("_count=%d", requestedAmount))); + + // get next batch + bundle = myClient.fetchResourceFromUrl(Bundle.class, nextUrl); + int received = bundle.getEntry().size(); + + // every next result should produce results + assertFalse(bundle.getEntry().isEmpty()); + count += received; + } else { + nextUrl = null; + } + } while (nextUrl != null); + + // verify + // we should receive all resources and linked resources + assertEquals(theResourceCount + theOrgCount, count); + } + + @Test public void testPagingWithIncludesReturnsConsistentValues() { // setup @@ -3204,7 +3298,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { }); myCaptureQueriesListener.logAllQueriesForCurrentThread(); - Bundle bundle = myClient.search().forResource("Patient").returnBundle(Bundle.class).execute(); + Bundle bundle = myClient + .search() + .forResource("Patient") + .returnBundle(Bundle.class) + .execute(); ourLog.debug("Result: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); assertEquals(2, bundle.getTotal()); assertEquals(1, bundle.getEntry().size()); 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 7bf94fc8e07..a0d630c9b12 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 @@ -215,7 +215,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = {TestR4Config.class}) +@ContextConfiguration(classes = { + TestR4Config.class +}) public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuilder { public static final String MY_VALUE_SET = "my-value-set"; public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; @@ -398,6 +400,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired @Qualifier("myOrganizationAffiliationDaoR4") protected IFhirResourceDao myOrganizationAffiliationDao; + @Autowired protected DatabaseBackedPagingProvider myPagingProvider; @Autowired diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java new file mode 100644 index 00000000000..5ce4a94655d --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java @@ -0,0 +1,21 @@ +package ca.uhn.fhir.jpa.test.config; + +import ca.uhn.fhir.jpa.config.HapiJpaConfig; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.mockito.Mockito.spy; + +/** + * This is a Test configuration class that allows spying underlying JpaConfigs beans + */ +@Configuration +public class TestHapiJpaConfig extends HapiJpaConfig { + + @Override + @Bean + public DatabaseBackedPagingProvider databaseBackedPagingProvider() { + return spy(super.databaseBackedPagingProvider()); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java index 98765d50c18..98942ba4d5a 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; -import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.PackageLoaderConfig; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; @@ -65,7 +64,7 @@ import static org.junit.jupiter.api.Assertions.fail; @Import({ JpaR4Config.class, PackageLoaderConfig.class, - HapiJpaConfig.class, + TestHapiJpaConfig.class, TestJPAConfig.class, TestHSearchAddInConfig.DefaultLuceneHeap.class, JpaBatch2Config.class, diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java index 5e1b96d63c7..77c02105714 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java @@ -119,6 +119,7 @@ public interface IBundleProvider { * server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example, * if the method is invoked with index 0,10 the method might return 10 search results, plus an * additional 20 resources which matched a client's _include specification. + *

    *

    * Note that if this bundle provider was loaded using a * page ID (i.e. via {@link ca.uhn.fhir.rest.server.IPagingProvider#retrieveResultList(RequestDetails, String, String)} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java index e9479ccd7ad..588f1909e7a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java @@ -43,6 +43,15 @@ public class SimpleBundleProvider implements IBundleProvider { private Integer myCurrentPageSize; private ResponsePage.ResponsePageBuilder myPageBuilder; + /** + * The actual number of resources we have tried to fetch. + * This value will only be populated if there is a + * _count query parameter provided. + * In which case, it will be the total number of resources + * we tried to fetch (should be _count + 1 for accurate paging) + */ + private int myTotalResourcesRequestedReturned = -1; + /** * Constructor */ @@ -144,6 +153,7 @@ public class SimpleBundleProvider implements IBundleProvider { @Override public List getResources( int theFromIndex, int theToIndex, @Nonnull ResponsePage.ResponsePageBuilder theResponsePageBuilder) { + theResponsePageBuilder.setTotalRequestedResourcesFetched(myTotalResourcesRequestedReturned); return (List) myList.subList(Math.min(theFromIndex, myList.size()), Math.min(theToIndex, myList.size())); } @@ -153,6 +163,10 @@ public class SimpleBundleProvider implements IBundleProvider { return myUuid; } + public void setTotalResourcesRequestedReturned(int theAmount) { + myTotalResourcesRequestedReturned = theAmount; + } + /** * Defaults to null */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java index 0d99af7ea91..b3815d66d45 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java @@ -105,8 +105,11 @@ public class ResponseBundleBuilder { pageSize = pagingCalculatePageSize(requestedPage, server.getPagingProvider()); Integer size = bundleProvider.size(); - numToReturn = - (size == null) ? pageSize : Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset); + if (size == null) { + numToReturn = pageSize; + } else { + numToReturn = Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset); + } resourceList = pagingBuildResourceList(theResponseBundleRequest, bundleProvider, numToReturn, responsePageBuilder); @@ -252,6 +255,7 @@ public class ResponseBundleBuilder { RestfulServerUtils.prettyPrintResponse(server, theResponseBundleRequest.requestDetails), theResponseBundleRequest.bundleType); + // set self link retval.setSelf(theResponseBundleRequest.linkSelf); // determine if we are using offset / uncached pages diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java index 5f797ffd435..7bfa3294938 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java @@ -71,6 +71,16 @@ public class ResponsePage { * even though it will change number of resources returned. */ private final int myOmittedResourceCount; + /** + * This is the total count of requested resources + * (ie, non-omitted, non-_include'd resource count). + * We typically fetch (for offset queries) 1 more than + * we need so we know if there is an additional page + * to fetch. + * But this is determined by the implementers of + * IBundleProvider. + */ + private final int myTotalRequestedResourcesFetched; /** * The bundle provider. @@ -109,6 +119,7 @@ public class ResponsePage { int theNumToReturn, int theIncludedResourceCount, int theOmittedResourceCount, + int theTotalRequestedResourcesFetched, IBundleProvider theBundleProvider) { mySearchId = theSearchId; myResourceList = theResourceList; @@ -116,6 +127,7 @@ public class ResponsePage { myNumToReturn = theNumToReturn; myIncludedResourceCount = theIncludedResourceCount; myOmittedResourceCount = theOmittedResourceCount; + myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched; myBundleProvider = theBundleProvider; myNumTotalResults = myBundleProvider.size(); @@ -190,24 +202,16 @@ public class ResponsePage { return StringUtils.isNotBlank(myBundleProvider.getNextPageId()); case NONCACHED_OFFSET: if (myNumTotalResults == null) { - /* - * Having a null total results is synonymous with - * having a next link. Once our results are exhausted, - * we will always have a myNumTotalResults value. - * - * Alternatively, if _total=accurate is provided, - * we'll also have a myNumTotalResults value. - */ - return true; + if (hasNextPageWithoutKnowingTotal()) { + return true; + } } else if (myNumTotalResults > myNumToReturn + ObjectUtils.defaultIfNull(myRequestedPage.offset, 0)) { return true; } break; case SAVED_SEARCH: if (myNumTotalResults == null) { - if (myPageSize == myResourceList.size() + myOmittedResourceCount - myIncludedResourceCount) { - // if the size of the resource list - included resources + omitted resources == pagesize - // we have more pages + if (hasNextPageWithoutKnowingTotal()) { return true; } } else if (myResponseBundleRequest.offset + myNumToReturn < myNumTotalResults) { @@ -220,6 +224,53 @@ public class ResponsePage { return false; } + /** + * If myNumTotalResults is null, it typically means we don't + * have an accurate total. + * + * Ie, we're in the middle of a set of pages (of non-named page results), + * and _total=accurate was not passed. + * + * This typically always means that a + * 'next' link definitely exists. + * + * But there are cases where this might not be true: + * * the last page of a search that also has an _include + * query parameter where the total of resources + _include'd + * resources is > the page size expected to be returned. + * * the last page of a search that returns the exact number + * of resources requested + * + * In these case, we must check to see if the returned + * number of *requested* resources. + * If our bundleprovider has fetched > requested, + * we'll know that there are more resources already. + * But if it hasn't, we'll have to check pagesize compared to + * _include'd count, omitted count, and resource count. + */ + private boolean hasNextPageWithoutKnowingTotal() { + // if we have totalRequestedResource count, and it's not equal to pagesize, + // then we can use this, alone, to determine if there are more pages + if (myTotalRequestedResourcesFetched >= 0) { + if (myPageSize < myTotalRequestedResourcesFetched) { + return true; + } + } else { + // otherwise we'll try and determine if there are next links based on the following + // calculation: + // resourceList.size - included resources + omitted resources == pagesize + // -> we (most likely) have more resources + if (myPageSize == myResourceList.size() - myIncludedResourceCount + myOmittedResourceCount) { + ourLog.warn( + "Returning a next page based on calculated resource count." + + " This could be inaccurate if the exact number of resources were fetched is equal to the pagesize requested. " + + " Consider setting ResponseBundleBuilder.setTotalResourcesFetchedRequest after fetching resources."); + return true; + } + } + return false; + } + public void setNextPageIfNecessary(BundleLinks theLinks) { if (hasNextPage()) { String next; @@ -356,9 +407,10 @@ public class ResponsePage { private int myIncludedResourceCount; private int myOmittedResourceCount; private IBundleProvider myBundleProvider; + private int myTotalRequestedResourcesFetched = -1; - public ResponsePageBuilder setToOmittedResourceCount(int theOmittedResourcesCountToAdd) { - myOmittedResourceCount = theOmittedResourcesCountToAdd; + public ResponsePageBuilder setOmittedResourceCount(int theOmittedResourceCount) { + myOmittedResourceCount = theOmittedResourceCount; return this; } @@ -392,6 +444,36 @@ public class ResponsePage { return this; } + public ResponsePageBuilder setTotalRequestedResourcesFetched(int theTotalRequestedResourcesFetched) { + myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched; + return this; + } + + /** + * Combine this builder with a second buider. + * Useful if a second page is requested, but you do not wish to + * overwrite the current values. + * + * Will not replace searchId, nor IBundleProvider (which should be + * the exact same for any subsequent searches anyways). + * + * Will also not copy pageSize nor numToReturn, as these should be + * the same for any single search result set. + * + * @param theSecondBuilder - a second builder (cannot be this one) + */ + public void combineWith(ResponsePageBuilder theSecondBuilder) { + assert theSecondBuilder != this; // don't want to combine with itself + + if (myTotalRequestedResourcesFetched != -1 && theSecondBuilder.myTotalRequestedResourcesFetched != -1) { + myTotalRequestedResourcesFetched += theSecondBuilder.myTotalRequestedResourcesFetched; + } + + // primitives can always be added + myIncludedResourceCount += theSecondBuilder.myIncludedResourceCount; + myOmittedResourceCount += theSecondBuilder.myOmittedResourceCount; + } + public ResponsePage build() { return new ResponsePage( mySearchId, // search id @@ -400,6 +482,7 @@ public class ResponsePage { myNumToReturn, // num to return myIncludedResourceCount, // included count myOmittedResourceCount, // omitted resources + myTotalRequestedResourcesFetched, // total count of requested resources myBundleProvider // the bundle provider ); } diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java index 0546cdd8cd5..b5e00d6839b 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java @@ -161,17 +161,24 @@ public class ResponsePageTest { */ @ParameterizedTest @CsvSource({ - "true,false,true", - "true,true,true", - "false,false,false", - "false,true,false", - "false,false,true", - "false,true,true" + "true,false,true,true", + "true,true,true,true", + "false,false,false,true", + "false,true,false,true", + "false,false,true,true", + "false,true,true,true", + "true,false,true,false", + "true,true,true,false", + "false,false,false,false", + "false,true,false,false", + "false,false,true,false", + "false,true,true,false" }) public void nonCachedOffsetPaging_setsNextPreviousLinks_test( boolean theNumTotalResultsIsNull, boolean theHasPreviousBoolean, - boolean theHasNextBoolean + boolean theHasNextBoolean, + boolean theHasTotalRequestedCountBool ) { // setup myBundleBuilder @@ -193,6 +200,11 @@ public class ResponsePageTest { } else { when(myBundleProvider.size()) .thenReturn(null); + if (theHasTotalRequestedCountBool) { + myBundleBuilder.setTotalRequestedResourcesFetched(11); // 1 more than pagesize + } else { + myBundleBuilder.setPageSize(10); + } } RequestedPage requestedPage = new RequestedPage( @@ -215,19 +227,28 @@ public class ResponsePageTest { @ParameterizedTest @CsvSource({ - "true,false,false", - "true,true,false", - "true,false,true", - "true,true,true", - "false,false,false", - "false,true,false", - "false,false,true", - "false,true,true" + "true,false,false,true", + "true,true,false,true", + "true,false,true,true", + "true,true,true,true", + "false,false,false,true", + "false,true,false,true", + "false,false,true,true", + "false,true,true,true", + "true,false,false,false", + "true,true,false,false", + "true,false,true,false", + "true,true,true,false", + "false,false,false,false", + "false,true,false,false", + "false,false,true,false", + "false,true,true,false" }) public void savedSearch_setsNextPreviousLinks_test( boolean theNumTotalResultsIsNull, boolean theHasPreviousBoolean, - boolean theHasNextBoolean + boolean theHasNextBoolean, + boolean theHasTotalRequestedFetched ) { // setup int pageSize = myList.size(); @@ -255,6 +276,12 @@ public class ResponsePageTest { if (!theHasNextBoolean) { myBundleBuilder.setNumToReturn(pageSize + offset + includeResourceCount); } + } else if (theHasTotalRequestedFetched) { + if (theHasNextBoolean) { + myBundleBuilder.setTotalRequestedResourcesFetched(pageSize + 1); // 1 more than page size + } else { + myBundleBuilder.setTotalRequestedResourcesFetched(pageSize); + } } // when diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java index c75342c414b..6e2396a6401 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java @@ -76,13 +76,10 @@ public class BulkDataImportProvider { public static final String PARAM_INPUT_TYPE = "type"; private static final Logger ourLog = LoggerFactory.getLogger(BulkDataImportProvider.class); - @Autowired private IJobCoordinator myJobCoordinator; - @Autowired private FhirContext myFhirCtx; - @Autowired private IRequestPartitionHelperSvc myRequestPartitionHelperService; private volatile List myResourceTypeOrder; @@ -94,14 +91,17 @@ public class BulkDataImportProvider { super(); } + @Autowired public void setJobCoordinator(IJobCoordinator theJobCoordinator) { myJobCoordinator = theJobCoordinator; } + @Autowired public void setFhirContext(FhirContext theCtx) { myFhirCtx = theCtx; } + @Autowired public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theRequestPartitionHelperSvc) { myRequestPartitionHelperService = theRequestPartitionHelperSvc; } From 469873aff7a573d9d894bc4e8ff2f6118bdd04b8 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Wed, 29 Nov 2023 09:16:41 -0500 Subject: [PATCH 50/86] Add migrator to uhnfhirtest (#5508) * Add migrator to uhnfhirtest * Fix spotless * Test fix --- .../HapiFlywayMigrateDatabaseCommandTest.java | 10 ++-- .../tasks/HapiFhirJpaMigrationTasks.java | 49 ++++++++++-------- .../ca/uhn/fhirtest/config/CommonConfig.java | 6 +++ .../uhn/fhirtest/config/TestAuditConfig.java | 2 +- .../uhn/fhirtest/config/TestDstu2Config.java | 2 +- .../uhn/fhirtest/config/TestDstu3Config.java | 2 +- .../ca/uhn/fhirtest/config/TestR4BConfig.java | 2 +- .../ca/uhn/fhirtest/config/TestR4Config.java | 2 +- .../ca/uhn/fhirtest/config/TestR5Config.java | 2 +- .../migrate/FhirTestAutoMigrator.java | 51 +++++++++++++++++++ 10 files changed, 98 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java index cebbf9c9a8b..31b74d0b5ab 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java @@ -5,11 +5,14 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils; import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity; +import ca.uhn.fhir.jpa.util.RandomTextUtils; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.base.Charsets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -35,10 +38,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +@TestMethodOrder(MethodOrderer.MethodName.class) public class HapiFlywayMigrateDatabaseCommandTest { private static final Logger ourLog = LoggerFactory.getLogger(HapiFlywayMigrateDatabaseCommandTest.class); - public static final String DB_DIRECTORY = "target/h2_test"; + private final String myDbDirectory = "target/h2_test/" + RandomTextUtils.newSecureRandomAlphaNumericString(5); static { HapiSystemProperties.enableTestMode(); @@ -252,12 +256,12 @@ public class HapiFlywayMigrateDatabaseCommandTest { @Nonnull private File getLocation(String theDatabaseName) throws IOException { - File directory = new File(DB_DIRECTORY); + File directory = new File(myDbDirectory); if (directory.exists()) { FileUtils.deleteDirectory(directory); } - return new File(DB_DIRECTORY + "/" + theDatabaseName); + return new File(myDbDirectory + "/" + theDatabaseName); } private void seedDatabase340(DriverTypeEnum.ConnectionProperties theConnectionProperties) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index a0f718dceb8..f480b23ed6e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -977,27 +977,34 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // Ugh. Only oracle supports using IDX_TAG_DEF_TP_CD_SYS to enforce this constraint. The others will // create another index. // For Sql Server, should change the index to be unique with include columns. Do this in 6.1 - tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE"); - Map addTagDefConstraint = new HashMap<>(); - addTagDefConstraint.put( - DriverTypeEnum.H2_EMBEDDED, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MARIADB_10_1, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MSSQL_2012, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MYSQL_5_7, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.ORACLE_12C, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.POSTGRES_9_4, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - version.executeRawSql("20220429.9", addTagDefConstraint); + // tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE"); + // Map addTagDefConstraint = new HashMap<>(); + // addTagDefConstraint.put( + // DriverTypeEnum.H2_EMBEDDED, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MARIADB_10_1, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MSSQL_2012, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MYSQL_5_7, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.ORACLE_12C, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.POSTGRES_9_4, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // version.executeRawSql("20220429.9", addTagDefConstraint); + version.addNop("20220429.9"); } // Fix for https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/328 diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java index b28a9367462..1d84422e1a2 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditEventSink; import ca.uhn.fhirtest.ScheduledSubscriptionDeleter; import ca.uhn.fhirtest.interceptor.AnalyticsInterceptor; import ca.uhn.fhirtest.joke.HolyFooCowInterceptor; +import ca.uhn.fhirtest.migrate.FhirTestAutoMigrator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -131,6 +132,11 @@ public class CommonConfig { return new FhirTestBalpAuditContextServices(); } + @Bean + public FhirTestAutoMigrator migrator() { + return new FhirTestAutoMigrator(); + } + public static boolean isLocalTestMode() { return "true".equalsIgnoreCase(System.getProperty("testmode.local")); } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java index bd6c6690f16..03e838b3296 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java @@ -120,7 +120,7 @@ public class TestAuditConfig { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index 7d957240c90..a3e4f203b2a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -138,7 +138,7 @@ public class TestDstu2Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index da6b07ac91b..0e4e285b9a2 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -138,7 +138,7 @@ public class TestDstu3Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java index 454faeab90a..bafbdc2daf3 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java @@ -136,7 +136,7 @@ public class TestR4BConfig { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index f95782ccc96..1c62b7a5225 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -139,7 +139,7 @@ public class TestR4Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java index 2870a925c32..e255f2627cb 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java @@ -148,7 +148,7 @@ public class TestR5Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java new file mode 100644 index 00000000000..0de60267015 --- /dev/null +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java @@ -0,0 +1,51 @@ +package ca.uhn.fhirtest.migrate; + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc; +import ca.uhn.fhir.jpa.migrate.MigrationTaskList; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; +import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; +import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; +import ca.uhn.fhir.util.VersionEnum; +import ca.uhn.fhirtest.config.CommonConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Properties; +import java.util.Set; +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +public class FhirTestAutoMigrator { + + private static final Logger ourLog = LoggerFactory.getLogger(FhirTestAutoMigrator.class); + public static final String MIGRATION_TABLENAME = "MIGRATIONS"; + + @Autowired + private DataSource myDataSource; + + @PostConstruct + public void run() { + DriverTypeEnum driver; + if (CommonConfig.isLocalTestMode()) { + driver = DriverTypeEnum.H2_EMBEDDED; + } else { + driver = DriverTypeEnum.POSTGRES_9_4; + } + + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(myDataSource, driver, MIGRATION_TABLENAME); + HapiMigrationStorageSvc hapiMigrationStorageSvc = new HapiMigrationStorageSvc(hapiMigrationDao); + + MigrationTaskList tasks = new HapiFhirJpaMigrationTasks(Set.of()).getAllTasks(VersionEnum.values()); + + SchemaMigrator schemaMigrator = new SchemaMigrator( + "HAPI FHIR", MIGRATION_TABLENAME, myDataSource, new Properties(), tasks, hapiMigrationStorageSvc); + schemaMigrator.setDriverType(driver); + + ourLog.info("About to run migration..."); + schemaMigrator.createMigrationTableIfRequired(); + schemaMigrator.migrate(); + ourLog.info("Migration complete"); + } +} From e15d0430d0aaac352f4e574773f84375129ef42e Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Wed, 29 Nov 2023 18:15:28 -0500 Subject: [PATCH 51/86] Oracle create index migration recovery (#5511) --- .../5511-oracle-migration-create-index.yaml | 6 ++ .../jpa/migrate/taskdef/AddIndexTaskTest.java | 70 +++++++++++++++++++ .../jpa/migrate/taskdef/AddIndexTask.java | 13 +++- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml create mode 100644 hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml new file mode 100644 index 00000000000..b9eda74e6d7 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5511 +title: "Previously, when creating an index as a part of a migration, if the index already existed with a different name +on Oracle, the migration would fail. This has been fixed so that the create index migration task now recovers with + a warning message if the index already exists with a different name." diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java new file mode 100644 index 00000000000..9e7d5f1ce71 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.test.util.LogbackCaptureTestExtension; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import oracle.jdbc.OracleDatabaseException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.jdbc.UncategorizedSQLException; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AddIndexTaskTest { + @Mock + DriverTypeEnum.ConnectionProperties myConnectionProperties; + @Mock + DataSource myDataSource; + @Mock + TransactionTemplate myTransactionTemplate; + + @RegisterExtension + LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) AddIndexTask.ourLog, Level.WARN); + + + @Test + void testOracleException() throws SQLException { + final AddIndexTask task = new AddIndexTask("1", "1"); + task.setColumns(Collections.singletonList("COLUMN_NAME")); + task.setUnique(true); + task.setIndexName("INDEX_NAME"); + task.setConnectionProperties(myConnectionProperties); + + when(myConnectionProperties.getDataSource()).thenReturn(myDataSource); + when(myConnectionProperties.getTxTemplate()).thenReturn(myTransactionTemplate); + + final String sql = "create index INDEX_NAME on TABLE_NAME (COLUMN_NAME)"; + when(myTransactionTemplate.execute(any())) + .thenReturn(Collections.emptySet()) + .thenThrow(new UncategorizedSQLException("ORA-01408: such column list already indexed", sql, new SQLException("ORA-01408: such column list already indexed", "72000", 1408))); + + myLogCapture.clearEvents(); + + // Red-green: this used to throw an exception. Now it logs a warning. + task.execute(); + + List events = myLogCapture.getLogEvents(); + assertThat(events, hasSize(1)); + LoggingEvent event = (LoggingEvent) events.get(0); + assertThat(event.getFormattedMessage(), containsString("ORA-01408: such column list already indexed")); + } +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java index b0d2ae6ccd4..d05b6784e06 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java @@ -37,7 +37,7 @@ import javax.annotation.Nonnull; public class AddIndexTask extends BaseTableTask { - private static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class); + static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class); private String myIndexName; private List myColumns; @@ -97,8 +97,15 @@ public class AddIndexTask extends BaseTableTask { try { executeSql(tableName, sql); } catch (Exception e) { - if (e.toString().contains("already exists")) { - ourLog.warn("Index {} already exists", myIndexName); + String message = e.toString(); + if (message.contains("already exists") + || + // The Oracle message is ORA-01408: such column list already indexed + // TODO KHS consider db-specific handling here that uses the error code instead of the message so + // this is language independent + // e.g. if the db is Oracle than checking e.getErrorCode() == 1408 should detect this case + message.contains("already indexed")) { + ourLog.warn("Index {} already exists: {}", myIndexName, e.getMessage()); } else { throw e; } From 8fbbaef0790aadb97fe92dba42ccc1d8da031ee5 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Thu, 30 Nov 2023 14:56:46 -0500 Subject: [PATCH 52/86] Add a new field to the CLIENT_RESPONSE pointcut in order to allow clients to mutate an HTTP response from the BaseClient. (#5488) * Add a new field to the CLIENT_RESPONSE pointcut in order to allow clients to mutate an HTTP response from the BaseClient. * Add FhirContext to ClientResponseContext. * Introduce a ModifiedStringApacheHttpResponse. Run spotless. * Remove TDOOs, add and update javadoc. * Spotless and copyright header. * Add changelog. * Unique error message code. * Only trigger the interceptor if the expected return type is a Bundle. * Fix spotless. * Code review feedback. * Spotless. * Bump to 6.11.3-SNAPSHOT --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../ca/uhn/fhir/interceptor/api/Pointcut.java | 7 +- .../client/api/ClientResponseContext.java | 103 +++++++++++++ hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- .../ModifiedStringApacheHttpResponse.java | 135 ++++++++++++++++++ .../uhn/fhir/rest/client/impl/BaseClient.java | 17 +++ hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- ...ent-response-pointcut-mutate-response.yaml | 6 + hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 82 files changed, 346 insertions(+), 80 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java create mode 100644 hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 14ae49d44f6..25f29f27be9 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index c8d934ef99a..6ffe01c5578 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 595f263a0df..e67b3027b07 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 994ea9cd1cf..64b7267f63f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut { *

  • * ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request *
  • + *
  • + * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient + * and also allows the client to mutate the contained IHttpResponse + *
  • * *

    * Hook methods must return void. @@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient"), + "ca.uhn.fhir.rest.client.api.IRestfulClient", + "ca.uhn.fhir.rest.client.api.ClientResponseContext"), /** * Server Hook: diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java new file mode 100644 index 00000000000..b0748bf2ab7 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java @@ -0,0 +1,103 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 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.client.api; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.Pointcut; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Used to pass context to {@link Pointcut#CLIENT_RESPONSE}, including a mutable {@link IHttpResponse} + */ +public class ClientResponseContext { + private final IHttpRequest myHttpRequest; + private IHttpResponse myHttpResponse; + private final IRestfulClient myRestfulClient; + private final FhirContext myFhirContext; + private final Class myReturnType; + + public ClientResponseContext( + IHttpRequest myHttpRequest, + IHttpResponse theHttpResponse, + IRestfulClient myRestfulClient, + FhirContext theFhirContext, + Class theReturnType) { + this.myHttpRequest = myHttpRequest; + this.myHttpResponse = theHttpResponse; + this.myRestfulClient = myRestfulClient; + this.myFhirContext = theFhirContext; + this.myReturnType = theReturnType; + } + + public IHttpRequest getHttpRequest() { + return myHttpRequest; + } + + public IHttpResponse getHttpResponse() { + return myHttpResponse; + } + + public IRestfulClient getRestfulClient() { + return myRestfulClient; + } + + public FhirContext getFhirContext() { + return myFhirContext; + } + + public Class getReturnType() { + return myReturnType; + } + + public void setHttpResponse(IHttpResponse theHttpResponse) { + this.myHttpResponse = theHttpResponse; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClientResponseContext that = (ClientResponseContext) o; + return Objects.equals(myHttpRequest, that.myHttpRequest) + && Objects.equals(myHttpResponse, that.myHttpResponse) + && Objects.equals(myRestfulClient, that.myRestfulClient) + && Objects.equals(myFhirContext, that.myFhirContext) + && Objects.equals(myReturnType, that.myReturnType); + } + + @Override + public int hashCode() { + return Objects.hash(myHttpRequest, myHttpResponse, myRestfulClient, myFhirContext, myReturnType); + } + + @Override + public String toString() { + return new StringJoiner(", ", ClientResponseContext.class.getSimpleName() + "[", "]") + .add("myHttpRequest=" + myHttpRequest) + .add("myHttpResponse=" + myHttpResponse) + .add("myRestfulClient=" + myRestfulClient) + .add("myFhirContext=" + myFhirContext) + .add("myReturnType=" + myReturnType) + .toString(); + } +} diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index d2c92c7102f..8b175d80acc 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index a09540f3cb5..9819c07f04a 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-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 6cdef901a35..cfcaebca7a9 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 0dfb56390d6..0bbc80ea717 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index d16c69b118e..86345f32a31 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 5b7c57a6612..0d687f7b1b0 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index a0acac6d5bc..0f0e49a5f1b 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java new file mode 100644 index 00000000000..28ea8163668 --- /dev/null +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java @@ -0,0 +1,135 @@ +/* + * #%L + * HAPI FHIR - Client Framework + * %% + * Copyright (C) 2014 - 2023 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.client.apache; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.util.StopWatch; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +/** + * Process a modified copy of an existing {@link IHttpResponse} with a String containing new content. + *

    + * Meant to be used with custom interceptors that need to hijack an existing IHttpResponse with new content. + */ +public class ModifiedStringApacheHttpResponse extends BaseHttpResponse implements IHttpResponse { + private static final org.slf4j.Logger ourLog = + org.slf4j.LoggerFactory.getLogger(ModifiedStringApacheHttpResponse.class); + private boolean myEntityBuffered = false; + private final String myNewContent; + private final IHttpResponse myOrigHttpResponse; + private byte[] myEntityBytes = null; + + public ModifiedStringApacheHttpResponse( + IHttpResponse theOrigHttpResponse, String theNewContent, StopWatch theResponseStopWatch) { + super(theResponseStopWatch); + myOrigHttpResponse = theOrigHttpResponse; + myNewContent = theNewContent; + } + + @Override + public void bufferEntity() throws IOException { + if (myEntityBuffered) { + return; + } + try (InputStream respEntity = readEntity()) { + if (respEntity != null) { + try { + myEntityBytes = IOUtils.toByteArray(respEntity); + } catch (IllegalStateException exception) { + throw new InternalErrorException(Msg.code(2447) + exception); + } + myEntityBuffered = true; + } + } + } + + @Override + public void close() { + if (myOrigHttpResponse instanceof CloseableHttpResponse) { + try { + ((CloseableHttpResponse) myOrigHttpResponse).close(); + } catch (IOException exception) { + ourLog.debug("Failed to close response", exception); + } + } + } + + @Override + public Reader createReader() throws IOException { + return new InputStreamReader(readEntity(), StandardCharsets.UTF_8); + } + + @Override + public Map> getAllHeaders() { + return myOrigHttpResponse.getAllHeaders(); + } + + @Override + public List getHeaders(String theName) { + return myOrigHttpResponse.getHeaders(theName); + } + + @Override + public String getMimeType() { + return myOrigHttpResponse.getMimeType(); + } + + @Override + public StopWatch getRequestStopWatch() { + return myOrigHttpResponse.getRequestStopWatch(); + } + + @Override + public Object getResponse() { + return null; + } + + @Override + public int getStatus() { + return myOrigHttpResponse.getStatus(); + } + + @Override + public String getStatusInfo() { + return myOrigHttpResponse.getStatusInfo(); + } + + @Override + public InputStream readEntity() { + if (myEntityBuffered) { + return new ByteArrayInputStream(myEntityBytes); + } else { + return new ByteArrayInputStream(myNewContent.getBytes()); + } + } +} diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 857fc4181b5..5aa920c920e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; @@ -352,12 +353,24 @@ public abstract class BaseClient implements IRestfulClient { response = httpRequest.execute(); + final Class returnType = (binding instanceof ResourceResponseHandler) + ? ((ResourceResponseHandler) binding).getReturnType() + : null; + + final ClientResponseContext clientResponseContext = + new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); + responseParams.add(ClientResponseContext.class, clientResponseContext); + getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); + // Replace the contents of the response with whatever the hook returned, or the same response as before if + // it no-op'd + response = clientResponseContext.getHttpResponse(); + String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; @@ -645,6 +658,10 @@ public abstract class BaseClient implements IRestfulClient { myAllowHtmlResponse = theAllowHtmlResponse; } + public Class getReturnType() { + return myReturnType; + } + @Override public T invokeClient( String theResponseMimeType, diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 199daabb89d..b92665512e4 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index ed159f4d302..0b3d6c514db 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index c8fb88de598..1031d739903 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml new file mode 100644 index 00000000000..31114533369 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5502 +jira: SMILE-7262 +title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response + to downstream processing." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 29520193ebd..5c60903d052 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index e217e6b86b9..3b065ee4a6e 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 79d5c793ed2..c2c94682ffa 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 1ebd7add4ce..0f2f3d46345 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 7e0dfd800ab..8180a9d47de 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index bac3221b4e4..05869115705 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 747bea9ba73..d470c942b64 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 699b9e6e1a8..60c8a22e5ca 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index dbcafa398b4..32074d95ef6 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 9e204079c6d..54a2010e026 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index c88d574ed07..92e6483c21a 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 - 6.11.2-SNAPSHOT + 6.11.3-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 b0408c3f65e..e11d32efadd 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 5ac8bc1e406..9431d68d8ae 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 7712a6347ba..afbf5b0acd2 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 41bcc967b15..b79574ffee6 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 4e0b6f578c5..a7070be80b0 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index f9cb2801cba..ae20eb92d4c 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 0bc7f2bb52a..2820eb69e50 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 13964da20d3..790ea7408be 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 43be75c5975..c940c9a1095 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 4b257238089..bb8306309e0 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 87545c43398..14226a4136d 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index d9bd49738d6..bb80f784fd3 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 - 6.11.2-SNAPSHOT + 6.11.3-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 088209cc351..8fd0cc9c89e 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 687afbf9a17..a0a001b2159 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 - 6.11.2-SNAPSHOT + 6.11.3-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 87ea52486a0..0f56910a478 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 13dd141193d..3e84d9f6b65 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.11.2-SNAPSHOT + 6.11.3-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 e6c86b9552e..bb234e607ea 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 - 6.11.2-SNAPSHOT + 6.11.3-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 f19a6bda58f..a576a9a2a4e 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 - 6.11.2-SNAPSHOT + 6.11.3-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 f88d87a0b98..58ce092f00d 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 - 6.11.2-SNAPSHOT + 6.11.3-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 2e09e1b0da9..0804a3fe706 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 - 6.11.2-SNAPSHOT + 6.11.3-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 a8ea0668e3d..719aa8d1726 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 - 6.11.2-SNAPSHOT + 6.11.3-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 6da838865ba..527947da607 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 5308ca8d290..3fa3a852d1c 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index ad912b14e25..809ed62b838 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 665b79c1640..9ab3b2f2903 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 5c28ea9bcd4..a470df0d3f5 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index e502c1a2cd8..7f32f86eae4 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 592593f7fb6..eb8714f900e 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index e6437a1f45d..ece2c90b097 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index fb29ef17446..49c62f02771 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index e6c24290983..15157a0e06b 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-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 3db931c5d24..cb78fbabc92 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index b606a900cd7..a082d97a136 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 6b64efeb95c..de42c6f8a55 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 - 6.11.2-SNAPSHOT + 6.11.3-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 f0e3e2214b8..fccff9966f2 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 95bf17a3aa3..e9d282a5a0a 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 14a7cbc6011..7db28482218 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 80ea9d8936b..2b0d862f79a 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 41e519eb390..90ea49f97e2 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index e12e18330c8..1179767e72c 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 97ea5b5fb54..8472e3bcd52 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 - 6.11.2-SNAPSHOT + 6.11.3-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 83b7ddb4706..59231fef232 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 - 6.11.2-SNAPSHOT + 6.11.3-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 c4c98ed5d6f..da2f4a4dcf0 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 - 6.11.2-SNAPSHOT + 6.11.3-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 63bf9d4eae2..2755e94c2dd 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 - 6.11.2-SNAPSHOT + 6.11.3-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 3d544b03e9d..f6f6ca40000 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 - 6.11.2-SNAPSHOT + 6.11.3-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 ec5a833c234..87408d2e414 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 80b4cc6a3a1..2d34f47aca0 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 1b06c741ab1..1f991cf66e9 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 339591a3322..9d249173349 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index cc15c9fe356..a9debe628e1 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.11.2-SNAPSHOT + 6.11.3-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 1af297a0476..91c7def0493 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 - 6.11.2-SNAPSHOT + 6.11.3-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 9996e300079..b07c2857bf4 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 - 6.11.2-SNAPSHOT + 6.11.3-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 249b5358d82..24120ff1236 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 - 6.11.2-SNAPSHOT + 6.11.3-SNAPSHOT ../../pom.xml From fab00ca3a06e39263be7d039fd9e150a1117ef5f Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 30 Nov 2023 16:43:10 -0500 Subject: [PATCH 53/86] Expose the desired Propagation in a tx template (#5516) --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-jpaserver-hfql/pom.xml | 2 +- hapi-fhir-jpaserver-ips/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4/pom.xml | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- .../util/JpaHapiTransactionServiceTest.java | 115 ++++++++++++++++++ hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage-cr/pom.xml | 2 +- hapi-fhir-storage-mdm/pom.xml | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- .../jpa/dao/tx/HapiTransactionService.java | 4 + hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r4b/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 2 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 79 files changed, 198 insertions(+), 79 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 25f29f27be9..7828ab2b979 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 6ffe01c5578..c80236908a1 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index e67b3027b07..ddb95cf5e41 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 8b175d80acc..80805431db2 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 9819c07f04a..75efb8b7607 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-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 cfcaebca7a9..953d36a6f18 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 0bbc80ea717..dd37903542f 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 86345f32a31..b2fe5bc90b3 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 0d687f7b1b0..70061eae755 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 0f0e49a5f1b..1f8a6b2d2f6 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index b92665512e4..bcaa65bb8cb 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 0b3d6c514db..bd14759edc9 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 1031d739903..ce9d6c844ce 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 5c60903d052..47c0a8dc304 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 3b065ee4a6e..e4b20f53161 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index c2c94682ffa..076d8328a69 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 0f2f3d46345..da09b2a7d82 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 8180a9d47de..6fcd53ab9ab 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 05869115705..1d71e2ac1e7 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index d470c942b64..5c28e18021a 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 60c8a22e5ca..2fb8fa175ad 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 32074d95ef6..e161b491e6b 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 54a2010e026..051a3069018 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 92e6483c21a..9b119ecea50 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 - 6.11.3-SNAPSHOT + 6.11.4-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 e11d32efadd..177675ad5a5 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 9431d68d8ae..83dde3e9706 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index afbf5b0acd2..79a1bddbeaa 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index b79574ffee6..df49d1e866a 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index a7070be80b0..3e7ea400eca 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index ae20eb92d4c..a1bf33ca001 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java new file mode 100644 index 00000000000..9f3d588d923 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java @@ -0,0 +1,115 @@ +package ca.uhn.fhir.jpa.util; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import ca.uhn.fhir.jpa.test.BaseJpaTest; +import ca.uhn.fhir.jpa.test.config.TestR4Config; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Propagation; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + TestR4Config.class +}) +public class JpaHapiTransactionServiceTest extends BaseJpaTest { + @Autowired PlatformTransactionManager myTxManager; + @Autowired IFhirResourceDao myPatientDao; + @Autowired IFhirResourceDao myObservationDao; + SystemRequestDetails myRequestDetails = new SystemRequestDetails(); + final AtomicReference myObservationId = new AtomicReference<>(); + final AtomicReference myPatientId = new AtomicReference<>(); + + @Override + protected FhirContext getFhirContext() { + return myFhirContext; + } + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + @Autowired + HapiTransactionService myHapiTransactionService; + + @Test + void testNewTransactionCommitInsideOldTransactionRollback() { + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRES_NEW) + .execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId())); + // roll back the Observation. The Patient has committed + throw new RuntimeException("roll back the Observation."); + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + assertFound(myPatientDao, myPatientId.get()); + } + + + + @Test + void testRequiredTransactionCommitInsideExistingTx_rollsBackWithMainTx() { + // given + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId())); + throw new RuntimeException("roll back both."); + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + assertNotFound(myPatientDao, myPatientId.get()); + } + + @Test + void testTransactionCommitRespectsRollbackOnly() { + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute((theTransactionStatus)->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + theTransactionStatus.setRollbackOnly(); + return null; + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + } + + void assertNotFound(IFhirResourceDao theDao, IIdType id) { + assertThrows(ResourceNotFoundException.class, ()-> theDao.read(id, myRequestDetails)); + } + + void assertFound(IFhirResourceDao theDao, IIdType theId) { + assertNotNull(theDao.read(theId, myRequestDetails)); + } +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 2820eb69e50..539f240c9a6 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 790ea7408be..4ab4abd28ef 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index c940c9a1095..35f51c77d5b 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index bb8306309e0..5d58f58def4 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 14226a4136d..aaa176420d4 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index bb80f784fd3..2efd21502bb 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 - 6.11.3-SNAPSHOT + 6.11.4-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 8fd0cc9c89e..3db18cec3bb 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index a0a001b2159..341ac8b5cfd 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 - 6.11.3-SNAPSHOT + 6.11.4-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 0f56910a478..ef1e667f1e8 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 3e84d9f6b65..b2ffa705596 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.11.3-SNAPSHOT + 6.11.4-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 bb234e607ea..627cefbce72 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 - 6.11.3-SNAPSHOT + 6.11.4-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 a576a9a2a4e..1c49210557f 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 - 6.11.3-SNAPSHOT + 6.11.4-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 58ce092f00d..e7e3ddb3c43 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 - 6.11.3-SNAPSHOT + 6.11.4-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 0804a3fe706..90d0122dc8c 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 - 6.11.3-SNAPSHOT + 6.11.4-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 719aa8d1726..ee7267fda57 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 - 6.11.3-SNAPSHOT + 6.11.4-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 527947da607..9c442072b80 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 3fa3a852d1c..4e9363295a7 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 809ed62b838..53ddb291362 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 9ab3b2f2903..87c80e9aea2 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index a470df0d3f5..ab92cdf2bff 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 7f32f86eae4..5329617d4fc 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index eb8714f900e..aaa18119b78 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index ece2c90b097..2af9a4e6dbc 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 49c62f02771..9a46e263b1e 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 15157a0e06b..d5a2887d24f 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index 58a7ee5c679..348dce762be 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -489,6 +489,10 @@ public class HapiTransactionService implements IHapiTransactionService { public RequestDetails getRequestDetailsForTesting() { return myRequestDetails; } + + public Propagation getPropagation() { + return myPropagation; + } } /** diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index cb78fbabc92..58a42fc146f 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index a082d97a136..94b617d3ad7 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index de42c6f8a55..380194ed555 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 - 6.11.3-SNAPSHOT + 6.11.4-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 fccff9966f2..4f4e5b841be 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index e9d282a5a0a..b537170eff6 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index 7db28482218..41b8084fbc6 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 2b0d862f79a..45390f8b466 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 90ea49f97e2..6da65fbc247 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 1179767e72c..e4e80515b04 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 8472e3bcd52..efe3441f8b6 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 - 6.11.3-SNAPSHOT + 6.11.4-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 59231fef232..d93073774c7 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 - 6.11.3-SNAPSHOT + 6.11.4-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 da2f4a4dcf0..1edb8b6baff 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 - 6.11.3-SNAPSHOT + 6.11.4-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 2755e94c2dd..3bfc892fb39 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 - 6.11.3-SNAPSHOT + 6.11.4-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 f6f6ca40000..cb8e68e7ae2 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 - 6.11.3-SNAPSHOT + 6.11.4-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 87408d2e414..9598eebf464 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 2d34f47aca0..6713e8e8969 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 1f991cf66e9..bcad4078b61 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 9d249173349..abe72e3b4d9 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index a9debe628e1..c0d8d46983e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.11.3-SNAPSHOT + 6.11.4-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 91c7def0493..e3d6f1f5c15 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 - 6.11.3-SNAPSHOT + 6.11.4-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 b07c2857bf4..88a64d42780 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 - 6.11.3-SNAPSHOT + 6.11.4-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 24120ff1236..4ad712cf23d 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 - 6.11.3-SNAPSHOT + 6.11.4-SNAPSHOT ../../pom.xml From ee4ecacbdb9a96e63ddd3f75afeaea44cf781582 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Thu, 30 Nov 2023 18:10:05 -0500 Subject: [PATCH 54/86] Revert changes for 5502. --- .../java/ca/uhn/fhir/interceptor/api/Pointcut.java | 7 +------ .../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 11 ----------- ...ient-client-response-pointcut-mutate-response.yaml | 6 ------ 3 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 64b7267f63f..994ea9cd1cf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -93,10 +93,6 @@ public enum Pointcut implements IPointcut { *

  • * ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request *
  • - *
  • - * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient - * and also allows the client to mutate the contained IHttpResponse - *
  • * *

    * Hook methods must return void. @@ -105,8 +101,7 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient", - "ca.uhn.fhir.rest.client.api.ClientResponseContext"), + "ca.uhn.fhir.rest.client.api.IRestfulClient"), /** * Server Hook: diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 5aa920c920e..4d05511d724 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -353,24 +353,13 @@ public abstract class BaseClient implements IRestfulClient { response = httpRequest.execute(); - final Class returnType = (binding instanceof ResourceResponseHandler) - ? ((ResourceResponseHandler) binding).getReturnType() - : null; - - final ClientResponseContext clientResponseContext = - new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); - responseParams.add(ClientResponseContext.class, clientResponseContext); getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); - // Replace the contents of the response with whatever the hook returned, or the same response as before if - // it no-op'd - response = clientResponseContext.getHttpResponse(); - String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml deleted file mode 100644 index 31114533369..00000000000 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: add -issue: 5502 -jira: SMILE-7262 -title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response - to downstream processing." From 8237c0795d74b43d6676b92041188cec04a5fcda Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Thu, 30 Nov 2023 18:23:13 -0500 Subject: [PATCH 55/86] test change --- .../src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 4d05511d724..f9bcbd48a00 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,7 +36,6 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; From 0c8258f93ce55dc4febc6f4cdc6dcd9f1b0b179b Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Thu, 30 Nov 2023 18:54:49 -0500 Subject: [PATCH 56/86] Revert the revert --- .../java/ca/uhn/fhir/interceptor/api/Pointcut.java | 7 ++++++- .../ca/uhn/fhir/rest/client/impl/BaseClient.java | 12 ++++++++++++ ...ent-client-response-pointcut-mutate-response.yaml | 6 ++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 994ea9cd1cf..64b7267f63f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut { *
  • * ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request *
  • + *
  • + * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient + * and also allows the client to mutate the contained IHttpResponse + *
  • * *

    * Hook methods must return void. @@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient"), + "ca.uhn.fhir.rest.client.api.IRestfulClient", + "ca.uhn.fhir.rest.client.api.ClientResponseContext"), /** * Server Hook: diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index f9bcbd48a00..5aa920c920e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; @@ -352,13 +353,24 @@ public abstract class BaseClient implements IRestfulClient { response = httpRequest.execute(); + final Class returnType = (binding instanceof ResourceResponseHandler) + ? ((ResourceResponseHandler) binding).getReturnType() + : null; + + final ClientResponseContext clientResponseContext = + new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); + responseParams.add(ClientResponseContext.class, clientResponseContext); getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); + // Replace the contents of the response with whatever the hook returned, or the same response as before if + // it no-op'd + response = clientResponseContext.getHttpResponse(); + String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml new file mode 100644 index 00000000000..31114533369 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5502 +jira: SMILE-7262 +title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response + to downstream processing." From 59936ee70ca13dc5c393e2d42c3cbb6a9bb52005 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Fri, 1 Dec 2023 04:02:54 -0500 Subject: [PATCH 57/86] Revert 5502. (#5521) --- .../main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 3 +-- .../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 8 -------- ...e-client-client-response-pointcut-mutate-response.yaml | 6 ------ 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 64b7267f63f..388f65b4c33 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -105,8 +105,7 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient", - "ca.uhn.fhir.rest.client.api.ClientResponseContext"), + "ca.uhn.fhir.rest.client.api.IRestfulClient"), /** * Server Hook: diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 5aa920c920e..8acd05255c2 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,7 +36,6 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; @@ -357,20 +356,13 @@ public abstract class BaseClient implements IRestfulClient { ? ((ResourceResponseHandler) binding).getReturnType() : null; - final ClientResponseContext clientResponseContext = - new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); - responseParams.add(ClientResponseContext.class, clientResponseContext); getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); - // Replace the contents of the response with whatever the hook returned, or the same response as before if - // it no-op'd - response = clientResponseContext.getHttpResponse(); - String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml deleted file mode 100644 index 31114533369..00000000000 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -type: add -issue: 5502 -jira: SMILE-7262 -title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response - to downstream processing." From ec5402abd4222b2cc486fdf55d70b712e2481c52 Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Fri, 1 Dec 2023 10:16:56 -0500 Subject: [PATCH 58/86] Restore changes for 5502. (#5525) * Restore changes for 5502. * Spotless. --- .../main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 3 ++- .../java/ca/uhn/fhir/rest/client/impl/BaseClient.java | 8 ++++++++ ...e-client-client-response-pointcut-mutate-response.yaml | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 388f65b4c33..64b7267f63f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -105,7 +105,8 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient"), + "ca.uhn.fhir.rest.client.api.IRestfulClient", + "ca.uhn.fhir.rest.client.api.ClientResponseContext"), /** * Server Hook: diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 8acd05255c2..5aa920c920e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; @@ -356,13 +357,20 @@ public abstract class BaseClient implements IRestfulClient { ? ((ResourceResponseHandler) binding).getReturnType() : null; + final ClientResponseContext clientResponseContext = + new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); + responseParams.add(ClientResponseContext.class, clientResponseContext); getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); + // Replace the contents of the response with whatever the hook returned, or the same response as before if + // it no-op'd + response = clientResponseContext.getHttpResponse(); + String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml new file mode 100644 index 00000000000..31114533369 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5502 +jira: SMILE-7262 +title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response + to downstream processing." From d597cf2763f55c55f21d9dab857f67619d5aca05 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 1 Dec 2023 15:19:51 -0500 Subject: [PATCH 59/86] Allow chained searches in Bundles where the fullUrl is fully qualified (#5529) * Allow chained searches in Bundles where the fullUrl is fully qualified * Add changelog * Spotless --- ...-in-bundle-where-fullurl-is-qualified.yaml | 4 ++ .../extractor/BaseSearchParamExtractor.java | 19 +++++- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 65 ++++++++++++------- 3 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml new file mode 100644 index 00000000000..2fdbb4cb53d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5529 +title: "When using a chained SearchParameter to search within a Bundle as [described here](https://smilecdr.com/docs/fhir_storage_relational/chained_searches_and_sorts.html#document-and-message-search-parameters), if the `Bundle.entry.fullUrl` was fully qualified but the reference was not, the search did not work. This has been corrected." diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index a4a4425071c..8ed714eb385 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -63,6 +63,7 @@ import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.IdType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -2010,17 +2011,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor * references within a Bundle */ if (theAppContext instanceof IBaseBundle && isNotBlank(theUrl) && !theUrl.startsWith("#")) { + String unqualifiedVersionlessReference; + boolean isPlaceholderReference; + if (theUrl.startsWith("urn:")) { + isPlaceholderReference = true; + unqualifiedVersionlessReference = null; + } else { + isPlaceholderReference = false; + unqualifiedVersionlessReference = + new IdType(theUrl).toUnqualifiedVersionless().getValue(); + } + List entries = BundleUtil.toListOfEntries(getContext(), (IBaseBundle) theAppContext); for (BundleEntryParts next : entries) { if (next.getResource() != null) { - if (theUrl.startsWith("urn:uuid:")) { + if (isPlaceholderReference) { if (theUrl.equals(next.getUrl()) || theUrl.equals( next.getResource().getIdElement().getValue())) { return (T) next.getResource(); } } else { - if (theUrl.equals(next.getResource().getIdElement().getValue())) { + if (unqualifiedVersionlessReference.equals(next.getResource() + .getIdElement() + .toUnqualifiedVersionless() + .getValue())) { return (T) next.getResource(); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 779cd067177..826f6ffad95 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -5793,8 +5793,15 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { * [base]/Bundle?composition.patient.identifier=foo */ @ParameterizedTest - @CsvSource({"urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", "Patient/ABC"}) - public void testCreateAndSearchForFullyChainedSearchParameter(String thePatientId) { + @CsvSource({ + "true , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", + "false, urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", + "true , Patient/ABC , Patient/ABC ", + "false, Patient/ABC , Patient/ABC ", + "true , Patient/ABC , http://example.com/fhir/Patient/ABC ", + "false, Patient/ABC , http://example.com/fhir/Patient/ABC ", + }) + public void testCreateAndSearchForFullyChainedSearchParameter(boolean theUseFullChainInName, String thePatientId, String theFullUrl) { // Setup 1 myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); @@ -5819,13 +5826,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { composition.setSubject(new Reference(thePatientId)); Patient patient = new Patient(); - patient.setId(new IdType(thePatientId)); + patient.setId(new IdType(theFullUrl)); patient.addIdentifier().setSystem("http://foo").setValue("bar"); Bundle bundle = new Bundle(); bundle.setType(Bundle.BundleType.DOCUMENT); - bundle.addEntry().setResource(composition); - bundle.addEntry().setResource(patient); + bundle + .addEntry() + .setResource(composition); + bundle + .addEntry() + .setFullUrl(theFullUrl) + .setResource(patient); myBundleDao.create(bundle, mySrd); @@ -5833,35 +5845,40 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { bundle2.setType(Bundle.BundleType.DOCUMENT); myBundleDao.create(bundle2, mySrd); - // Verify 1 - runInTransaction(() -> { + // Test + + SearchParameterMap map; + if (theUseFullChainInName) { + map = SearchParameterMap.newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar")); + } else { + map = SearchParameterMap.newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar")); + } + IBundleProvider outcome = myBundleDao.search(map, mySrd); + + // Verify + + List params = extractAllTokenIndexes(); + assertThat(params.toString(), params, containsInAnyOrder( + "composition.patient.identifier http://foo|bar" + )); + assertEquals(1, outcome.size()); + } + + private List extractAllTokenIndexes() { + List params = runInTransaction(() -> { logAllTokenIndexes(); - List params = myResourceIndexedSearchParamTokenDao + return myResourceIndexedSearchParamTokenDao .findAll() .stream() .filter(t -> t.getParamName().contains(".")) .map(t -> t.getParamName() + " " + t.getSystem() + "|" + t.getValue()) .toList(); - assertThat(params.toString(), params, containsInAnyOrder( - "composition.patient.identifier http://foo|bar" - )); }); - - // Test 2 - IBundleProvider outcome; - - SearchParameterMap map = SearchParameterMap - .newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar")); - outcome = myBundleDao.search(map, mySrd); - assertEquals(1, outcome.size()); - - map = SearchParameterMap - .newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar")); - outcome = myBundleDao.search(map, mySrd); - assertEquals(1, outcome.size()); + return params; } + @Nested public class TagBelowTests { From 113cf4002d91408f13741d7c69ef2bf25e66a76d Mon Sep 17 00:00:00 2001 From: Luke deGruchy Date: Fri, 1 Dec 2023 17:00:05 -0500 Subject: [PATCH 60/86] First commit with very rough partial solution and test. --- .../ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java index c7616bde567..702430aa4b6 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java @@ -44,7 +44,7 @@ public abstract class BaseTableColumnTask extends BaseTableTask { } public BaseTableColumnTask setColumnName(String theColumnName) { - myColumnName = theColumnName.toUpperCase(); + myColumnName = theColumnName.toLowerCase(); return this; } From 6d58df0f44d093e7a8b42903f7fbcb07c5c83673 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 3 Dec 2023 11:15:37 -0500 Subject: [PATCH 61/86] Jakarta/Javax Servlet Migration (#5429) * Servlet migrsation changes * Apply spotless * Work on migration * Start working on JPA migration * Test fixes * Test fixes * Test fixes * Work * Work on servlet migration * Work on migration * Work on fixes * Imports fix * Work on tests * Test fix * All tests passing * Work on migration * Schema generator * Refactor ID strategy * Some test fixes * Test fixes * API additions * Work on websockets * Test fixe * Version bump * Work on migration * All tests have passed! * Work on test fixes * Fixes * Work on fixes * CDR fix * Work on migration * Fixes * Spotless * Disable onetoone ban * Adjust migratyions * Transaction service fix * Add tinyint to migrator * Work * Migration * Compile fix * Add license * Test fix * Test fix * Cleanup * Cleanup attempt * Test fix * Reslve fixmes * Remove legacy lastn implementation * Test cleanup * Cleanup * Remove dead import * Bad import setups * Review comments * Review fixes * Build fix * Test fix * Spotless * Account for CR changes * Compile fixes * Rollback one change * Test tweak * License header * Test fix * Test fix part 2: * Clean up tx isolation * Spotless * Spotless apply * Drop min SQL Server level * Attempted test fix * Test fix * Test fix * Test fix * Bump hibernate * Test fix * Test fixes * Test fixes * Test fixes * Test cleanup * One more test fix * Test fix * Try to fix test * Clean up expunge * Test fix * Remove unneeded task * Spotless * Bump compiler plugin * Spotless * Compile fix * Add license headers * License header * Test fixes * Bump animal sniffer * Spotless * Bump Jetty version * Test fix and version bump * Cleanup --------- Co-authored-by: Tadgh --- .mvn/wrapper/MavenWrapperDownloader.java | 13 +- hapi-deployable-pom/pom.xml | 11 +- hapi-fhir-android/pom.xml | 8 +- hapi-fhir-base/pom.xml | 17 +- .../context/support/LookupCodeRequest.java | 19 + .../ca/uhn/fhir/interceptor/api/Pointcut.java | 54 +- .../ca/uhn/fhir/model/api/BaseElement.java | 6 +- .../ca/uhn/fhir/model/api/IFhirVersion.java | 9 +- .../narrative2/NarrativeTemplateManifest.java | 11 +- .../uhn/fhir/rest/annotation/Operation.java | 4 +- .../ca/uhn/fhir/rest/annotation/Read.java | 5 +- .../uhn/fhir/rest/annotation/Transaction.java | 5 +- .../rest/annotation/TransactionParam.java | 5 +- .../ca/uhn/fhir/rest/annotation/Validate.java | 5 +- .../fhir/rest/gclient/TokenClientParam.java | 4 +- .../uhn/fhir/rest/param/ParamPrefixEnum.java | 4 +- .../java/ca/uhn/fhir/util/AttachmentUtil.java | 6 +- .../java/ca/uhn/fhir/util/rdf/RDFUtil.java | 3 +- .../schematron/SchematronBaseValidator.java | 9 +- .../ca/uhn/fhir/i18n/HapiLocalizerTest.java | 10 +- .../api/ResourceMetadataKeyEnumTest.java | 3 +- .../java/ca/uhn/fhir/model/api/TagTest.java | 5 +- .../rest/api/CacheControlDirectiveTest.java | 4 +- .../uhn/fhir/rest/api/EncodingEnumTest.java | 2 +- .../uhn/fhir/rest/api/MethodOutcomeTest.java | 3 +- .../ca/uhn/fhir/rest/param/DateParamTest.java | 4 +- .../fhir/rest/param/QualifierDetailsTest.java | 3 +- .../uhn/fhir/rest/param/StringParamTest.java | 12 +- .../BaseServerResponseExceptionTest.java | 2 +- .../ca/uhn/fhir/util/DateRangeUtilTest.java | 5 +- .../java/ca/uhn/fhir/util/LogUtilTest.java | 6 +- .../ca/uhn/fhir/util/ReflectionUtilTest.java | 12 +- .../validation/ResultSeverityEnumTest.java | 4 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 4 +- .../src/checkstyle/hapi-base-checkstyle.xml | 7 + hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 74 +- .../ca/uhn/fhir/cli/BulkImportCommand.java | 12 +- .../fhir/cli/ReindexTerminologyCommand.java | 3 +- .../fhir/cli/WebsocketSubscribeCommand.java | 47 +- ...mandIT.java => BulkImportCommandTest.java} | 19 +- .../HapiClearMigrationLockCommandTest.java | 9 +- .../HapiFlywayMigrateDatabaseCommandTest.java | 3 +- .../fhir/cli/HeaderPassthroughOptionTest.java | 39 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 7 +- .../okhttp/GenericOkHttpClientDstu2Test.java | 869 ++++++++--------- hapi-fhir-client/pom.xml | 2 +- .../rest/client/apache/ApacheHttpClient.java | 16 +- .../client/apache/ApacheHttpResponse.java | 16 +- .../rest/client/apache/BaseHttpClient.java | 4 +- .../client/apache/GZipContentInterceptor.java | 4 +- .../interceptor/BasicAuthInterceptor.java | 4 +- .../client/interceptor/CookieInterceptor.java | 4 +- .../rest/client/method/BaseMethodBinding.java | 40 +- .../BaseOutcomeReturningMethodBinding.java | 4 +- ...indingWithResourceIdButNoResourceBody.java | 7 +- .../client/method/ConditionalParamBinder.java | 4 +- .../client/method/DeleteMethodBinding.java | 5 +- .../client/method/HistoryMethodBinding.java | 4 +- .../rest/client/method/NullParameter.java | 4 +- .../client/method/OperationMethodBinding.java | 8 +- .../client/method/PatchMethodBinding.java | 11 +- .../rest/client/method/RawParamsParmeter.java | 4 +- .../rest/client/method/ReadMethodBinding.java | 13 +- .../rest/client/method/SearchParameter.java | 61 +- .../client/method/TransactionParameter.java | 8 +- hapi-fhir-converter/pom.xml | 38 +- .../VersionedApiConverterInterceptor.java | 4 +- ...ersionedApiConverterInterceptorR4Test.java | 53 +- hapi-fhir-dist/pom.xml | 16 +- hapi-fhir-docs/pom.xml | 10 +- .../fhir/docs/AuthorizationInterceptors.java | 9 +- .../AuthorizingTesterUiClientFactory.java | 3 +- .../ca/uhn/hapi/fhir/docs/BalpExample.java | 2 +- .../fhir/docs/ClientTransactionExamples.java | 8 +- .../java/ca/uhn/hapi/fhir/docs/Copier.java | 6 +- .../ca/uhn/hapi/fhir/docs/Dstu2Examples.java | 2 +- .../hapi/fhir/docs/ExampleRestfulClient.java | 3 +- .../hapi/fhir/docs/ExampleRestfulServlet.java | 4 +- .../uhn/hapi/fhir/docs/ExtensionsDstu3.java | 9 +- .../uhn/hapi/fhir/docs/FhirContextIntro.java | 6 +- .../ca/uhn/hapi/fhir/docs/FhirDataModel.java | 12 +- .../fhir/docs/JaxRsConformanceProvider.java | 10 +- .../fhir/docs/JaxRsPatientRestProvider.java | 22 +- .../uhn/hapi/fhir/docs/PartitionExamples.java | 2 +- .../fhir/docs/RequestCounterInterceptor.java | 5 +- .../docs/RequestExceptionInterceptor.java | 4 +- .../RestfulPatientResourceProviderMore.java | 63 +- .../hapi/fhir/docs/SecurityInterceptors.java | 5 +- .../hapi/fhir/docs/ServerETagExamples.java | 5 +- .../uhn/hapi/fhir/docs/ServerOperations.java | 10 +- .../uhn/hapi/fhir/docs/ServletExamples.java | 4 +- .../uhn/hapi/fhir/docs/ValidatorExamples.java | 2 +- .../hapi/fhir/changelog/7_0_0/changes.yaml | 25 + .../fhir/docs/server_jpa/database_support.md | 25 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 27 +- .../fhir/jaxrs/client/JaxRsHttpClient.java | 14 +- .../fhir/jaxrs/client/JaxRsHttpRequest.java | 8 +- .../fhir/jaxrs/client/JaxRsHttpResponse.java | 6 +- .../client/JaxRsRestfulClientFactory.java | 6 +- .../server/AbstractJaxRsBundleProvider.java | 16 +- .../AbstractJaxRsConformanceProvider.java | 12 +- .../server/AbstractJaxRsPageProvider.java | 12 +- .../jaxrs/server/AbstractJaxRsProvider.java | 12 +- .../server/AbstractJaxRsResourceProvider.java | 15 +- .../JaxRsExceptionInterceptor.java | 8 +- .../interceptor/JaxRsResponseException.java | 3 +- .../fhir/jaxrs/server/util/JaxRsRequest.java | 4 +- .../fhir/jaxrs/server/util/JaxRsResponse.java | 4 +- .../client/GenericJaxRsClientDstu2Test.java | 915 ++++++++--------- .../client/GenericJaxRsClientDstu3Test.java | 916 ++++++++---------- .../client/JaxRsRestfulClientFactoryTest.java | 4 +- .../ca/uhn/fhir/jaxrs/client/MyFilter.java | 8 +- ...xRsConformanceProviderDstu2Hl7OrgTest.java | 6 +- ...ctJaxRsConformanceProviderDstu2_1Test.java | 8 +- ...ractJaxRsConformanceProviderDstu3Test.java | 6 +- ...bstractJaxRsConformanceProviderR4Test.java | 6 +- .../AbstractJaxRsConformanceProviderTest.java | 6 +- .../server/AbstractJaxRsProviderTest.java | 8 +- ...bstractJaxRsResourceProviderDstu3Test.java | 4 +- .../AbstractJaxRsResourceProviderTest.java | 4 +- .../JaxRsExceptionInterceptorTest.java | 43 +- .../JaxRsResponseExceptionTest.java | 10 +- .../TestJaxRsConformanceRestProvider.java | 8 +- ...TestJaxRsConformanceRestProviderDstu3.java | 14 +- .../test/TestJaxRsMockPageProvider.java | 9 +- .../test/TestJaxRsMockPageProviderDstu3.java | 8 +- .../TestJaxRsMockPatientRestProvider.java | 54 +- ...xRsMockPatientRestProviderDstu2Hl7Org.java | 10 +- ...stJaxRsMockPatientRestProviderDstu2_1.java | 47 +- ...TestJaxRsMockPatientRestProviderDstu3.java | 10 +- .../TestJaxRsMockPatientRestProviderR4.java | 47 +- .../server/util/JaxRsRequestDstu3Test.java | 6 +- .../jaxrs/server/util/JaxRsRequestTest.java | 7 +- .../server/util/JaxRsResponseDstu3Test.java | 2 +- .../jaxrs/server/util/JaxRsResponseTest.java | 2 +- hapi-fhir-jpa/pom.xml | 12 +- ...ocalContainerEntityManagerFactoryBean.java | 9 +- .../jpa/sched/BaseSchedulerServiceImpl.java | 2 +- .../util/DerbyTenSevenHapiFhirDialect.java | 33 +- .../jpa/util}/ISequenceValueMassager.java | 10 +- hapi-fhir-jpaserver-base/pom.xml | 191 ++-- .../uhn/fhir/jpa/batch2/JpaBatch2Config.java | 3 +- .../jpa/batch2/JpaJobPersistenceImpl.java | 7 +- .../DatabaseBlobBinaryStorageSvcImpl.java | 6 +- ...BulkDataExportJobSchedulingHelperImpl.java | 2 +- .../export/svc/JpaBulkExportProcessor.java | 2 +- .../bulk/imprt/svc/BulkDataImportSvcImpl.java | 2 +- .../fhir/jpa/config/Batch2SupportConfig.java | 3 +- .../fhir/jpa/config/EnversAuditConfig.java | 3 +- .../config/HapiFhirHibernateJpaDialect.java | 2 +- .../ca/uhn/fhir/jpa/config/JpaConfig.java | 12 +- .../util/HapiEntityManagerFactoryUtil.java | 66 +- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 20 +- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 17 +- .../fhir/jpa/dao/BaseHapiFhirSystemDao.java | 18 +- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 2 +- .../fhir/jpa/dao/FulltextSearchSvcImpl.java | 6 +- .../ca/uhn/fhir/jpa/dao/HistoryBuilder.java | 51 +- ...JpaPersistedResourceValidationSupport.java | 2 +- .../jpa/dao/JpaResourceDaoCodeSystem.java | 2 +- .../jpa/dao/JpaResourceDaoComposition.java | 2 +- .../fhir/jpa/dao/JpaResourceDaoEncounter.java | 2 +- .../jpa/dao/JpaResourceDaoObservation.java | 73 +- .../fhir/jpa/dao/JpaResourceDaoPatient.java | 2 +- .../dao/ObservationLastNIndexPersistSvc.java | 260 ----- .../fhir/jpa/dao/TransactionProcessor.java | 36 +- .../dao/data/IResourceHistoryTableDao.java | 10 +- .../data/custom/IResourceTableDaoImpl.java | 4 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 2 +- .../dao/expunge/ExpungeEverythingService.java | 75 +- .../dao/index/DaoSearchParamSynchronizer.java | 18 +- .../fhir/jpa/dao/index/IdHelperService.java | 36 +- ...rchParamWithInlineReferencesExtractor.java | 6 +- .../fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java | 64 +- .../fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java | 8 +- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 2 +- .../fhir/jpa/dao/r4b/FhirSystemDaoR4B.java | 2 +- .../uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java | 2 +- .../uhn/fhir/jpa/dao/search/PathContext.java | 74 ++ .../delete/DeleteConflictFinderService.java | 8 +- .../delete/batch2/DeleteExpungeSvcImpl.java | 2 +- .../jpa/entity/Batch2JobInstanceEntity.java | 26 +- .../jpa/entity/Batch2WorkChunkEntity.java | 32 +- .../entity/BulkExportCollectionEntity.java | 26 +- .../BulkExportCollectionFileEntity.java | 22 +- .../fhir/jpa/entity/BulkExportJobEntity.java | 28 +- .../fhir/jpa/entity/BulkImportJobEntity.java | 26 +- .../jpa/entity/BulkImportJobFileEntity.java | 24 +- .../jpa/entity/HapiFhirEnversRevision.java | 14 +- .../java/ca/uhn/fhir/jpa/entity/MdmLink.java | 38 +- .../uhn/fhir/jpa/entity/PartitionEntity.java | 11 +- .../jpa/entity/ResourceReindexJobEntity.java | 18 +- .../fhir/jpa/entity/ResourceSearchView.java | 16 +- .../java/ca/uhn/fhir/jpa/entity/Search.java | 43 +- .../ca/uhn/fhir/jpa/entity/SearchInclude.java | 23 +- .../ca/uhn/fhir/jpa/entity/SearchResult.java | 2 +- .../fhir/jpa/entity/SubscriptionTable.java | 2 +- .../uhn/fhir/jpa/entity/TermCodeSystem.java | 2 +- .../jpa/entity/TermCodeSystemVersion.java | 30 +- .../ca/uhn/fhir/jpa/entity/TermConcept.java | 38 +- .../jpa/entity/TermConceptDesignation.java | 24 +- .../uhn/fhir/jpa/entity/TermConceptMap.java | 2 +- .../fhir/jpa/entity/TermConceptMapGroup.java | 2 +- .../entity/TermConceptMapGroupElement.java | 2 +- .../TermConceptMapGroupElementTarget.java | 2 +- .../entity/TermConceptParentChildLink.java | 31 +- .../fhir/jpa/entity/TermConceptProperty.java | 32 +- .../ca/uhn/fhir/jpa/entity/TermValueSet.java | 38 +- .../fhir/jpa/entity/TermValueSetConcept.java | 30 +- .../TermValueSetConceptDesignation.java | 14 +- .../jpa/entity/TermValueSetConceptView.java | 8 +- .../entity/TermValueSetConceptViewOracle.java | 8 +- .../tasks/HapiFhirJpaMigrationTasks.java | 4 + .../fhir/jpa/packages/JpaPackageCache.java | 28 +- .../jpa/packages/PackageInstallerSvcImpl.java | 2 +- .../jpa/partition/PartitionLookupSvcImpl.java | 2 +- .../BaseJpaResourceProviderCodeSystem.java | 2 +- .../BaseJpaResourceProviderComposition.java | 2 +- .../BaseJpaResourceProviderConceptMap.java | 3 +- .../BaseJpaResourceProviderEncounter.java | 4 +- ...BaseJpaResourceProviderEncounterDstu2.java | 4 +- .../BaseJpaResourceProviderObservation.java | 4 +- .../BaseJpaResourceProviderPatient.java | 4 +- .../jpa/provider/BaseJpaSystemProvider.java | 2 +- .../provider/JpaConformanceProviderDstu2.java | 2 +- .../jpa/provider/ProcessMessageProvider.java | 3 +- .../provider/TerminologyUploaderProvider.java | 6 +- .../provider/ValueSetOperationProvider.java | 2 +- .../ValueSetOperationProviderDstu2.java | 2 +- .../dstu3/JpaConformanceProviderDstu3.java | 2 +- .../r4/MemberMatchR4ResourceProvider.java | 2 +- .../HapiHSearchAnalysisConfigurers.java | 58 +- .../search/PersistedJpaBundleProvider.java | 4 +- .../fhir/jpa/search/ResourceSearchUrlSvc.java | 2 +- .../jpa/search/SynchronousSearchSvcImpl.java | 2 +- .../jpa/search/builder/SearchBuilder.java | 25 +- .../BaseQuantityPredicateBuilder.java | 2 +- .../builder/sql/SearchQueryBuilder.java | 13 +- .../builder/sql/SearchQueryExecutor.java | 21 +- .../jpa/search/builder/tasks/SearchTask.java | 2 - .../cache/DatabaseSearchCacheSvcImpl.java | 2 +- .../lastn/ElasticsearchRestClientFactory.java | 15 +- .../search/lastn/ElasticsearchSvcImpl.java | 790 +-------------- .../jpa/search/lastn/IElasticsearchSvc.java | 57 -- .../reindex/ResourceReindexingSvcImpl.java | 10 +- .../jpa/search/warm/CacheWarmingSvcImpl.java | 2 +- .../term/TermCodeSystemStorageSvcImpl.java | 6 +- .../term/TermConceptClientMappingSvcImpl.java | 18 +- .../ca/uhn/fhir/jpa/term/TermReadSvcImpl.java | 12 +- .../jpa/util/PersistenceContextProvider.java | 4 +- .../fhir/jpa/util/QueryParameterUtils.java | 6 +- .../jpa/util/ScrollableResultsIterator.java | 2 +- .../validation/JpaValidationSupportChain.java | 5 +- .../index/DaoSearchParamSynchronizerTest.java | 2 +- .../pom.xml | 42 +- .../config/ElasticsearchWithPrefixConfig.java | 34 +- .../jpa/dao/r4/ElasticsearchPrefixTest.java | 20 +- .../FhirResourceDaoR4SearchLastNAsyncIT.java | 17 +- .../r4/FhirResourceDaoR4SearchLastNIT.java | 58 +- ...LastNUsingExtendedHSearchIndexAsyncIT.java | 27 - ...earchLastNUsingExtendedHSearchIndexIT.java | 60 -- ...esourceDaoR4SearchWithElasticSearchIT.java | 2 +- .../fhir/jpa/dao/r4/HSearchSandboxTest.java | 2 +- ...bservationIndexedSearchParamLastNR4IT.java | 474 --------- .../TokenAutocompleteElasticsearchIT.java | 2 +- ...lasticsearchSvcMultipleObservationsIT.java | 511 ---------- ...tNElasticsearchSvcSingleObservationIT.java | 262 ----- .../search/lastn/LastNTestDataGenerator.java | 144 --- hapi-fhir-jpaserver-hfql/pom.xml | 6 +- .../fql/jdbc/RemoteHfqlExecutionResult.java | 2 +- .../jpa/fql/provider/HfqlRestProvider.java | 4 +- hapi-fhir-jpaserver-ips/pom.xml | 6 +- .../uhn/fhir/jpa/ips/api/SectionRegistry.java | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 6 +- .../mdm/broker/MdmQueueConsumerLoader.java | 3 +- .../jpa/mdm/svc/MdmControllerSvcImpl.java | 8 +- .../fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java | 3 +- .../fhir/jpa/mdm/helper/BaseMdmHelper.java | 2 +- .../MdmSearchExpandingInterceptorIT.java | 2 +- .../interceptor/MdmStorageInterceptorIT.java | 6 +- .../jpa/mdm/provider/BaseProviderR4Test.java | 2 + .../mdm/provider/MdmOperationPointcutsIT.java | 19 +- .../MdmProviderCrossPartitionR4Test.java | 3 +- .../fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java | 1 - .../clear/MdmLinkSlowDeletionSandboxIT.java | 6 +- hapi-fhir-jpaserver-model/pom.xml | 8 +- .../dialect/HapiFhirCockroachDialect.java | 42 + .../model/dialect/HapiFhirDerbyDialect.java | 42 + .../jpa/model/dialect/HapiFhirH2Dialect.java | 45 +- .../model/dialect/HapiFhirMariaDBDialect.java | 42 + .../model/dialect/HapiFhirMySQLDialect.java | 42 + .../model/dialect/HapiFhirOracleDialect.java | 42 + .../dialect/HapiFhirPostgres94Dialect.java | 14 +- .../dialect/HapiFhirPostgresDialect.java | 38 + .../dialect/HapiFhirSQLServerDialect.java | 42 + .../dialect/HapiSequenceStyleGenerator.java | 29 +- .../entity/AuditableBasePartitionable.java | 6 +- .../jpa/model/entity/BaseHasResource.java | 14 +- .../jpa/model/entity/BasePartitionable.java | 7 +- .../jpa/model/entity/BaseResourceIndex.java | 2 +- .../BaseResourceIndexedSearchParam.java | 10 +- ...aseResourceIndexedSearchParamQuantity.java | 5 +- .../ca/uhn/fhir/jpa/model/entity/BaseTag.java | 9 +- .../jpa/model/entity/BinaryStorageEntity.java | 15 +- .../uhn/fhir/jpa/model/entity/ForcedId.java | 28 +- .../jpa/model/entity/NpmPackageEntity.java | 24 +- .../model/entity/NpmPackageVersionEntity.java | 37 +- .../NpmPackageVersionResourceEntity.java | 36 +- .../entity/PartitionablePartitionId.java | 4 +- ...sistedResourceModifiedMessageEntityPK.java | 5 +- .../ResourceHistoryProvenanceEntity.java | 23 +- .../model/entity/ResourceHistoryTable.java | 7 +- .../jpa/model/entity/ResourceHistoryTag.java | 27 +- .../ResourceIndexedComboStringUnique.java | 3 +- .../ResourceIndexedComboTokenNonUnique.java | 25 +- .../ResourceIndexedSearchParamCoords.java | 27 +- .../ResourceIndexedSearchParamDate.java | 32 +- .../ResourceIndexedSearchParamNumber.java | 31 +- .../ResourceIndexedSearchParamQuantity.java | 26 +- ...eIndexedSearchParamQuantityNormalized.java | 26 +- .../ResourceIndexedSearchParamString.java | 25 +- .../ResourceIndexedSearchParamToken.java | 32 +- .../entity/ResourceIndexedSearchParamUri.java | 27 +- .../fhir/jpa/model/entity/ResourceLink.java | 30 +- .../model/entity/ResourceModifiedEntity.java | 13 +- .../model/entity/ResourceSearchUrlEntity.java | 15 +- .../fhir/jpa/model/entity/ResourceTable.java | 38 +- .../fhir/jpa/model/entity/ResourceTag.java | 27 +- .../entity/SearchParamPresentEntity.java | 2 +- .../jpa/model/entity/StorageSettings.java | 2 +- .../fhir/jpa/model/entity/TagDefinition.java | 29 +- .../uhn/fhir/jpa/model/util/JpaConstants.java | 1 - hapi-fhir-jpaserver-searchparam/pom.xml | 10 +- .../model/ReadPartitionIdRequestDetails.java | 2 +- .../fhir/jpa/cache/IResourceChangeEvent.java | 2 +- .../jpa/cache/IResourceChangeListener.java | 2 +- .../cache/IResourceChangeListenerCache.java | 2 +- ...IResourceChangeListenerCacheRefresher.java | 2 +- .../IResourceChangeListenerRegistry.java | 2 +- .../fhir/jpa/cache/IResourceVersionSvc.java | 2 +- .../fhir/jpa/cache/ResourceChangeEvent.java | 2 +- .../cache/ResourceChangeListenerCache.java | 2 +- .../ResourceChangeListenerCacheFactory.java | 2 +- ...ourceChangeListenerCacheRefresherImpl.java | 2 +- .../ResourceChangeListenerRegistryImpl.java | 2 +- ...urceChangeListenerRegistryInterceptor.java | 5 +- .../fhir/jpa/cache/ResourceChangeResult.java | 2 +- .../jpa/cache/ResourcePersistentIdMap.java | 2 +- .../fhir/jpa/cache/ResourceVersionCache.java | 2 +- .../fhir/jpa/cache/ResourceVersionMap.java | 2 +- .../partition/IRequestPartitionHelperSvc.java | 2 +- .../fhir/jpa/searchparam/MatchUrlService.java | 2 +- .../jpa/searchparam/ResourceMetaParams.java | 2 +- .../fhir/jpa/searchparam/ResourceSearch.java | 2 +- .../jpa/searchparam/SearchParamConstants.java | 2 +- .../jpa/searchparam/SearchParameterMap.java | 2 +- .../config/NicknameServiceConfig.java | 2 +- .../searchparam/config/SearchParamConfig.java | 2 +- .../extractor/BaseSearchParamExtractor.java | 4 +- .../CrossPartitionReferenceDetails.java | 2 +- .../extractor/GeopointNormalizer.java | 2 +- .../extractor/IResourceLinkResolver.java | 2 +- .../extractor/ISearchParamExtractor.java | 2 +- .../extractor/LogicalReferenceHelper.java | 2 +- .../jpa/searchparam/extractor/PathAndRef.java | 2 +- .../ResourceIndexedSearchParamComposite.java | 2 +- .../ResourceIndexedSearchParams.java | 2 +- .../extractor/SearchParamExtractorDstu2.java | 2 +- .../extractor/SearchParamExtractorDstu3.java | 4 +- .../extractor/SearchParamExtractorR4.java | 4 +- .../extractor/SearchParamExtractorR4B.java | 4 +- .../extractor/SearchParamExtractorR5.java | 4 +- .../SearchParamExtractorService.java | 2 +- .../StringTrimmingTrimmerMatcher.java | 2 +- .../AuthorizationSearchParamMatcher.java | 2 +- .../matcher/InMemoryMatchResult.java | 2 +- .../matcher/InMemoryResourceMatcher.java | 2 +- .../matcher/IndexedSearchParamExtractor.java | 2 +- .../matcher/SearchParamMatcher.java | 2 +- .../nickname/NicknameInterceptor.java | 2 +- .../SearchableHashMapResourceProvider.java | 2 +- .../registry/ISearchParamProvider.java | 2 +- .../ISearchParamRegistryController.java | 2 +- .../registry/JpaSearchParamCache.java | 2 +- .../registry/ReadOnlySearchParamCache.java | 2 +- .../registry/RuntimeSearchParamCache.java | 2 +- .../registry/SearchParamRegistryImpl.java | 6 +- .../SearchParameterCanonicalizer.java | 2 +- .../fhir/jpa/searchparam/retry/Retrier.java | 2 +- .../searchparam/util/Dstu3DistanceHelper.java | 2 +- .../jpa/searchparam/util/JpaParamUtil.java | 2 +- .../util/LastNParameterHelper.java | 2 +- .../util/RuntimeSearchParamHelper.java | 2 +- .../util/SearchParameterHelper.java | 2 +- .../jpa/searchparam/util/SourceParam.java | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 13 +- .../match/deliver/email/EmailDetails.java | 4 +- .../SubscriptionWebsocketHandler.java | 4 +- .../MatchingQueueSubscriberLoader.java | 3 +- .../match/registry/SubscriptionRegistry.java | 2 +- .../SubscriptionSubmitInterceptorLoader.java | 2 +- .../SubscriptionTriggeringSvcImpl.java | 2 +- ...castingSubscribableChannelWrapperTest.java | 2 +- ...aseSubscriptionDeliverySubscriberTest.java | 4 +- .../module/SubscriptionTestConfig.java | 2 +- ...kingQueueSubscribableChannelDstu3Test.java | 6 +- hapi-fhir-jpaserver-test-dstu2/pom.xml | 2 +- .../jpa/dao/dstu2/BaseJpaDstu2SystemTest.java | 6 +- .../fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java | 2 +- .../FhirResourceDaoDstu2SearchFtTest.java | 2 +- .../FhirResourceDaoDstu2SearchNoFtTest.java | 2 +- .../jpa/provider/SubscriptionsDstu2Test.java | 14 +- .../jpa/provider/SystemProviderDstu2Test.java | 4 +- ...temProviderTransactionSearchDstu2Test.java | 4 +- .../resthook/RestHookTestDstu2Test.java | 4 +- ...rRegisteredToStorageSettingsDstu2Test.java | 4 +- hapi-fhir-jpaserver-test-dstu3/pom.xml | 2 +- .../jpa/dao/dstu3/BaseJpaDstu3SystemTest.java | 6 +- .../FhirResourceDaoDstu3SearchFtTest.java | 2 +- .../FhirResourceDaoDstu3SearchNoFtTest.java | 2 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 2 +- .../jpa/packages/IgInstallerDstu3Test.java | 37 +- .../uhn/fhir/jpa/packages/NpmDstu3Test.java | 65 +- .../dstu3/SubscriptionsDstu3Test.java | 14 +- ...temProviderTransactionSearchDstu3Test.java | 4 +- .../resthook/RestHookTestDstu3Test.java | 6 +- ...rRegisteredToStorageSettingsDstu3Test.java | 4 +- .../SubscriptionTriggeringDstu3Test.java | 6 +- hapi-fhir-jpaserver-test-r4/pom.xml | 13 +- .../jpa/batch2/Batch2JobMaintenanceIT.java | 2 +- .../binstore/BinaryAccessProviderTest.java | 6 +- .../bulk/imprt2/ConsumeFilesStepR4Test.java | 4 +- .../uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java | 18 +- .../jpa/dao/BaseHapiFhirResourceDaoTest.java | 4 +- .../jpa/dao/TransactionProcessorTest.java | 4 +- .../expunge/ResourceTableFKProviderTest.java | 6 +- .../fhir/jpa/dao/r4/BaseJpaR4SystemTest.java | 4 +- .../r4/FhirResourceDaoR4QueryCountTest.java | 19 +- .../dao/r4/FhirResourceDaoR4SearchFtTest.java | 2 +- .../r4/FhirResourceDaoR4SearchNoFtTest.java | 33 +- .../FhirResourceDaoR4SearchOptimizedTest.java | 18 +- .../r4/FhirResourceDaoR4SearchSqlTest.java | 6 +- .../dao/r4/FhirResourceDaoR4UpdateTest.java | 2 +- .../fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 2 +- .../jpa/dao/r4/PartitioningSqlR4Test.java | 38 +- .../fhir/jpa/delete/job/ReindexJobTest.java | 9 +- ...adingDeleteInterceptorMultiThreadTest.java | 4 +- .../ForceOffsetSearchModeInterceptorTest.java | 8 +- .../PartitioningInterceptorR4Test.java | 2 +- .../PatientIdPartitionInterceptorTest.java | 6 +- .../ca/uhn/fhir/jpa/packages/NpmR4Test.java | 44 +- .../fhir/jpa/packages/NpmSearchR4Test.java | 4 +- .../packages/loader/PackageLoaderSvcIT.java | 37 +- ...rtitionedSubscriptionTriggeringR4Test.java | 2 +- .../jpa/provider/r4/EmptyIndexesR4Test.java | 4 +- .../provider/r4/MultitenantServerR4Test.java | 2 +- ...minologyServiceResourceProviderR4Test.java | 2 +- ...sourceProviderCustomSearchParamR4Test.java | 8 +- .../r4/ResourceProviderInterceptorR4Test.java | 2 +- ...oviderOnlySomeResourcesProvidedR4Test.java | 4 +- ...sourceProviderR4RemoteTerminologyTest.java | 2 +- .../provider/r4/ResourceProviderR4Test.java | 12 +- ...rceProviderR4ValueSetNoVerCSNoVerTest.java | 8 +- .../jpa/provider/r4/SubscriptionsR4Test.java | 14 +- .../jpa/provider/r4/SystemProviderR4Test.java | 4 +- ...SystemProviderTransactionSearchR4Test.java | 4 +- .../stresstest/GiantTransactionPerfTest.java | 29 +- .../subscription/BaseSubscriptionsR4Test.java | 2 +- ...tivatesPreExistingSubscriptionsR4Test.java | 6 +- ...ptorRegisteredToStorageSettingsR4Test.java | 4 +- .../uhn/fhir/jpa/term/ITermReadSvcTest.java | 4 +- ...erminologySvcImplCurrentVersionR4Test.java | 4 +- .../ReindexTerminologyHSearchR4Test.java | 2 +- hapi-fhir-jpaserver-test-r4b/pom.xml | 2 +- .../uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java | 2 +- .../BaseSubscriptionsR4BTest.java | 2 +- hapi-fhir-jpaserver-test-r5/pom.xml | 2 +- .../ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java | 2 +- .../r5/FhirSystemDaoTransactionR5Test.java | 2 +- .../subscription/BaseSubscriptionsR5Test.java | 8 +- hapi-fhir-jpaserver-test-utilities/pom.xml | 50 +- .../HapiEmbeddedDatabasesExtension.java | 23 +- ...nterceptorReadPartitionsBasedOnScopes.java | 2 +- .../uhn/fhir/jpa/packages/FakeNpmServlet.java | 6 +- .../CodeSystemLookupWithPropertiesUtil.java | 19 + .../r4/BaseResourceProviderR4Test.java | 4 +- .../jpa/subscription/NotificationServlet.java | 9 +- .../subscription/SocketImplementation.java | 18 +- .../uhn/fhir/jpa/test/BaseJpaDstu3Test.java | 2 +- .../ca/uhn/fhir/jpa/test/BaseJpaR4Test.java | 42 +- .../ca/uhn/fhir/jpa/test/BaseJpaTest.java | 2 +- .../BaseValueSetHSearchExpansionR4Test.java | 2 +- .../fhir/jpa/test/config/TestDstu2Config.java | 9 +- .../fhir/jpa/test/config/TestDstu3Config.java | 9 +- .../test/config/TestHSearchAddInConfig.java | 2 +- .../jpa/test/config/TestHapiJpaConfig.java | 19 + .../fhir/jpa/test/config/TestJPAConfig.java | 2 +- .../fhir/jpa/test/config/TestR4BConfig.java | 9 +- .../fhir/jpa/test/config/TestR4Config.java | 76 +- .../fhir/jpa/test/config/TestR5Config.java | 11 +- .../jpa/util/WebsocketSubscriptionClient.java | 26 +- .../jpa/config/DispatcherServletConfig.java | 2 +- .../HapiFhirHibernateJpaDialectTest.java | 4 +- .../jpa/dao/index/ResourceVersionSvcTest.java | 10 +- .../jpa/embedded/HapiSchemaMigrationTest.java | 4 + ...lIT.java => SchedulerServiceImplTest.java} | 6 +- .../SearchQueryBuilderDialectMySqlTest.java | 3 +- ...SearchQueryBuilderDialectPostgresTest.java | 7 +- ...earchQueryBuilderDialectSqlServerTest.java | 18 +- .../builder/sql/SearchQueryBuilderTest.java | 110 +-- .../jpa/term/LoincFullLoadR4SandboxIT.java | 8 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 70 +- .../ca/uhn/fhirtest/TestRestfulServer.java | 8 +- .../FhirTestBalpAuditContextServices.java | 3 +- .../uhn/fhirtest/config/FhirTesterConfig.java | 3 +- .../uhn/fhirtest/config/TestAuditConfig.java | 10 +- .../uhn/fhirtest/config/TestDstu2Config.java | 8 +- .../uhn/fhirtest/config/TestDstu3Config.java | 8 +- .../ca/uhn/fhirtest/config/TestR4BConfig.java | 8 +- .../ca/uhn/fhirtest/config/TestR4Config.java | 8 +- .../ca/uhn/fhirtest/config/TestR5Config.java | 8 +- .../interceptor/AnalyticsInterceptor.java | 2 +- .../fhirtest/joke/HolyFooCowInterceptor.java | 5 +- .../migrate/FhirTestAutoMigrator.java | 2 +- .../mvc/SubscriptionPlaygroundController.java | 2 +- .../java/ca/uhn/fhirtest/UhnFhirTestApp.java | 4 +- hapi-fhir-server-cds-hooks/pom.xml | 2 +- .../json/CdsServiceRequestContextJson.java | 3 + .../cdshooks/svc/CdsHooksContextBooter.java | 2 +- .../cdshooks/svc/CdsServiceRegistryImpl.java | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- .../fhir/mdm/provider/MdmProviderLoader.java | 3 +- hapi-fhir-server-openapi/pom.xml | 17 +- .../fhir/rest/openapi/OpenApiInterceptor.java | 10 +- .../rest/openapi/OpenApiInterceptorTest.java | 23 +- hapi-fhir-server/pom.xml | 6 +- .../rest/api/server/IRestfulResponse.java | 2 +- .../fhir/rest/api/server/RequestDetails.java | 9 +- .../server/storage/TransactionDetails.java | 11 +- .../server/ApacheProxyAddressStrategy.java | 4 +- .../HardcodedServerAddressStrategy.java | 5 +- .../rest/server/IServerAddressStrategy.java | 4 +- .../server/IServerConformanceProvider.java | 3 +- .../IncomingRequestAddressStrategy.java | 5 +- .../uhn/fhir/rest/server/ResourceBinding.java | 21 +- .../uhn/fhir/rest/server/RestfulServer.java | 18 +- .../fhir/rest/server/RestfulServerUtils.java | 41 +- .../rest/server/ServletRequestTracing.java | 4 +- .../BanUnsupportedHttpMethodsInterceptor.java | 4 +- .../server/interceptor/CorsInterceptor.java | 4 +- .../ExceptionHandlingInterceptor.java | 6 +- .../interceptor/IServerInterceptor.java | 10 +- .../interceptor/InterceptorAdapter.java | 6 +- .../interceptor/LoggingInterceptor.java | 6 +- .../RequestValidatingInterceptor.java | 4 +- .../ResponseHighlighterInterceptor.java | 6 +- .../SearchPreferHandlingInterceptor.java | 4 +- .../ServeMediaResourceRawInterceptor.java | 4 +- .../VerboseLoggingInterceptor.java | 4 +- .../auth/SearchNarrowingInterceptor.java | 4 +- .../BaseOutcomeReturningMethodBinding.java | 8 +- ...indingWithResourceIdButNoResourceBody.java | 6 +- .../BaseResourceReturningMethodBinding.java | 4 +- .../server/method/GraphQLMethodBinding.java | 4 +- .../fhir/rest/server/method/MethodUtil.java | 34 +- .../rest/server/method/SearchParameter.java | 64 +- .../rest/server/method/SortParameter.java | 8 +- .../server/method/SummaryEnumParameter.java | 5 +- .../ServerCapabilityStatementProvider.java | 4 +- .../server/servlet/ServletRequestDetails.java | 4 +- .../servlet/ServletRestfulResponse.java | 4 +- .../servlet/ServletSubRequestDetails.java | 5 +- .../server/util/ITestingUiClientFactory.java | 3 +- .../IncomingRequestAddressStrategyTest.java | 2 +- .../rest/server/SimpleBundleProviderTest.java | 2 +- .../server/interceptor/ConfigLoaderTest.java | 4 +- .../method/ConformanceMethodBindingTest.java | 2 +- .../server/method/MethodMatchEnumTest.java | 2 +- .../servlet/ServletRequestDetailsTest.java | 2 +- .../servlet/ServletRestfulResponseTest.java | 2 +- .../hapi-fhir-caching-api/pom.xml | 2 +- .../hapi-fhir-caching-caffeine/pom.xml | 4 +- .../hapi-fhir-caching-guava/pom.xml | 2 +- .../hapi-fhir-caching-testing/pom.xml | 2 +- hapi-fhir-serviceloaders/pom.xml | 2 +- .../pom.xml | 6 +- .../autoconfigure/FhirAutoConfiguration.java | 4 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../provider/PatientResourceProvider.java | 4 +- ...pleJerseyRestfulServerApplicationTest.java | 12 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 4 +- .../uhn/fhir/jpa/migrate/DriverTypeEnum.java | 14 +- .../ca/uhn/fhir/jpa/migrate/JdbcUtils.java | 19 +- .../migrate/entity/HapiMigrationEntity.java | 10 +- .../jpa/migrate/taskdef/ColumnTypeEnum.java | 8 +- .../ColumnTypeToDriverTypeToSqlType.java | 14 +- .../jpa/migrate/taskdef/AddColumnTest.java | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 6 +- .../jobs/export/BulkDataExportProvider.java | 2 +- .../jobs/imprt/BulkDataImportProvider.java | 2 +- .../jobs/imprt/BulkImportFileServlet.java | 6 +- .../jobs/imprt/BulkImportJobParameters.java | 8 +- .../fhir/batch2/jobs/BaseR4ServerTest.java | 4 +- .../imprt/BulkDataImportProviderTest.java | 2 +- .../pom.xml | 2 +- .../test/support/TestJobParameters.java | 2 +- hapi-fhir-storage-batch2/pom.xml | 20 +- .../coordinator/JobCoordinatorImpl.java | 4 +- .../JobParameterJsonValidator.java | 8 +- .../jobs/parameters/PartitionedUrl.java | 3 +- .../uhn/fhir/batch2/model/JobDefinition.java | 6 +- .../batch2/coordinator/TestJobParameters.java | 2 +- .../JobMaintenanceServiceImplTest.java | 4 +- hapi-fhir-storage-cr/pom.xml | 28 +- .../fhir/cr/dstu3/BaseCrDstu3TestServer.java | 4 +- .../ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java | 4 +- hapi-fhir-storage-mdm/pom.xml | 2 +- .../MdmSubmitterInterceptorLoader.java | 3 +- .../batch2/clear/MdmClearJobParameters.java | 2 +- hapi-fhir-storage-test-utilities/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 8 +- .../cache/BaseResourceCacheSynchronizer.java | 4 +- .../fhir/jpa/api/dao/IFhirResourceDao.java | 2 +- .../api/dao/IFhirResourceDaoComposition.java | 3 +- .../api/dao/IFhirResourceDaoEncounter.java | 3 +- .../api/dao/IFhirResourceDaoObservation.java | 3 +- .../jpa/api/dao/IFhirResourceDaoPatient.java | 3 +- .../binary/provider/BinaryAccessProvider.java | 4 +- .../ca/uhn/fhir/jpa/dao/ISearchBuilder.java | 2 +- .../dao/expunge/PartitionAwareSupplier.java | 6 +- .../jpa/dao/tx/HapiTransactionService.java | 29 +- .../SearchBuilderLoadIncludesParameters.java | 2 +- .../fhir/jpa/provider/BaseJpaProvider.java | 2 +- .../jpa/provider/BaseJpaResourceProvider.java | 4 +- .../provider/BaseStorageSystemProvider.java | 2 - .../impl/LinkedBlockingChannelFactory.java | 2 +- .../CircularQueueCaptureQueriesListener.java | 36 +- .../java/ca/uhn/fhir/jpa/util/SqlQuery.java | 3 + ...ncMemoryQueueBackedFhirClientBalpSink.java | 2 +- .../balp/BalpAuditCaptureInterceptor.java | 20 +- .../balp/IBalpAuditContextServices.java | 2 +- .../balp/BalpAuditCaptureInterceptorTest.java | 17 +- hapi-fhir-structures-dstu2.1/pom.xml | 69 +- .../server/ServerConformanceProvider.java | 8 +- .../rest/server/CreateBinaryDstu2_1Test.java | 172 ---- .../fhir/rest/server/CreateDstu2_1Test.java | 283 ------ .../rest/server/CustomTypeServerDstu2_1.java | 59 +- .../server/DeleteConditionalDstu2_1Test.java | 132 --- .../server/FormatParameterDstu2_1Test.java | 255 ----- .../MetadataConformanceDstu2_1Test.java | 191 ---- .../server/OperationServerDstu2_1Test.java | 786 --------------- ...ServerWithSearchParamTypesDstu2_1Test.java | 514 ---------- .../fhir/rest/server/PatchDstu2_1Test.java | 188 ---- .../uhn/fhir/rest/server/ReadDstu2_1Test.java | 122 --- .../server/SearchCountParamDstu2_1Test.java | 184 ---- .../fhir/rest/server/SearchDstu2_1Test.java | 142 --- .../server/SearchHasParamDstu2_1Test.java | 121 --- .../rest/server/SearchPostDstu2_1Test.java | 260 ----- .../rest/server/SearchSortDstu2_1Test.java | 132 --- .../SearchWithGenericListDstu2_1Test.java | 57 +- .../server/SearchWithIncludesDstu2_1Test.java | 135 --- ...hWithServerAddressStrategyDstu2_1Test.java | 172 ---- .../server/ServerExceptionDstu2_1Test.java | 137 --- .../server/ServerMimetypeDstu2_1Test.java | 422 -------- .../ServerUsingOldTypesDstu2_1Test.java | 2 +- ...nclassifiedServerExceptionDstu2_1Test.java | 108 --- .../fhir/rest/server/UpdateDstu2_1Test.java | 195 ---- .../fhir/rest/server/ValidateDstu2_1Test.java | 327 ------- hapi-fhir-structures-dstu2/pom.xml | 50 +- .../dstu2/ServerConformanceProvider.java | 4 +- ...tThymeleafNarrativeGeneratorDstu2Test.java | 2 +- .../apache/ApacheClientIntegrationTest.java | 57 +- .../uhn/fhir/rest/server/BinaryDstu2Test.java | 70 +- .../rest/server/BundleTypeInResponseTest.java | 49 +- .../rest/server/CompartmentDstu2Test.java | 63 +- .../rest/server/CreateConditionalTest.java | 66 +- .../uhn/fhir/rest/server/DeleteDstu2Test.java | 52 +- .../uhn/fhir/rest/server/ETagServerTest.java | 64 +- .../IncludeAndRevincludeParameterTest.java | 53 +- .../fhir/rest/server/IncludeDstu2Test.java | 70 +- .../OperationDuplicateServerDstu2Test.java | 56 +- .../rest/server/OperationServerDstu2Test.java | 114 +-- ...onServerWithSearchParamTypesDstu2Test.java | 68 +- .../rest/server/PatientResourceProvider.java | 2 +- .../uhn/fhir/rest/server/ReadDstu2Test.java | 66 +- ...archBundleProviderWithNoSizeDstu2Test.java | 4 +- .../server/SearchCountParamDstu2Test.java | 62 +- .../uhn/fhir/rest/server/SearchDstu2Test.java | 111 +-- ...rchReturningProfiledResourceDstu2Test.java | 75 +- .../server/SearchWithDstu2BundleTest.java | 51 +- .../SearchWithGenericListDstu2Test.java | 57 +- .../ServerConformanceProviderDstu2Test.java | 18 +- .../rest/server/ServerFeaturesDstu2Test.java | 99 +- .../rest/server/ServerSearchDstu2Test.java | 66 +- .../rest/server/SummaryParamDstu2Test.java | 71 +- ...ransactionWithBundleResourceParamTest.java | 4 +- ...ithVersionlessBundleResourceParamTest.java | 4 +- .../uhn/fhir/rest/server/UpdateDstu2Test.java | 57 +- .../fhir/rest/server/ValidateDstu2Test.java | 54 +- .../InterceptorUserDataMapDstu2Test.java | 59 +- .../LoggingInterceptorDstu2Test.java | 114 +-- .../ServerActionInterceptorTest.java | 136 ++- hapi-fhir-structures-dstu3/pom.xml | 52 +- .../ServerCapabilityStatementProvider.java | 4 +- .../context/ContextScanningDstu3Test.java | 42 +- ...tThymeleafNarrativeGeneratorDstu3Test.java | 2 +- .../rest/server/CreateBinaryDstu3Test.java | 53 +- .../uhn/fhir/rest/server/CreateDstu3Test.java | 67 +- .../rest/server/CustomTypeServerDstu3.java | 52 +- .../server/DeleteConditionalDstu3Test.java | 67 +- .../rest/server/FormatParameterDstu3Test.java | 86 +- .../rest/server/InterceptorDstu3Test.java | 80 +- .../MetadataCapabilityStatementDstu3Test.java | 73 +- .../server/MetadataConformanceDstu3Test.java | 68 +- .../rest/server/OperationServerDstu3Test.java | 96 +- ...onServerWithSearchParamTypesDstu3Test.java | 70 +- .../rest/server/PatchServerDstu3Test.java | 63 +- ...archBundleProviderWithNoSizeDstu3Test.java | 4 +- .../server/SearchCountParamDstu3Test.java | 62 +- .../server/SearchDefaultMethodDstu3Test.java | 58 +- .../uhn/fhir/rest/server/SearchDstu3Test.java | 71 +- .../rest/server/SearchHasParamDstu3Test.java | 48 +- .../fhir/rest/server/SearchPostDstu3Test.java | 68 +- .../fhir/rest/server/SearchSortDstu3Test.java | 48 +- .../SearchWithGenericListDstu3Test.java | 58 +- .../server/SearchWithIncludesDstu3Test.java | 50 +- ...rchWithServerAddressStrategyDstu3Test.java | 85 +- .../rest/server/ServerExceptionDstu3Test.java | 66 +- .../rest/server/ServerMimetypeDstu3Test.java | 77 +- .../server/ServerUsingOldTypesDstu3Test.java | 2 +- .../UnclassifiedServerExceptionDstu3Test.java | 49 +- .../fhir/rest/server/ValidateDstu3Test.java | 61 +- ...pprtedHttpMethodsInterceptorDstu3Test.java | 61 +- .../interceptor/CorsInterceptorDstu3Test.java | 4 +- .../AuthorizationInterceptorDstu3Test.java | 67 +- .../rest/server/PatientResourceProvider.java | 2 +- ...rCapabilityStatementProviderDstu3Test.java | 18 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 69 +- .../server/ServerConformanceProvider.java | 4 +- .../rest/server/BinaryHl7OrgDstu2Test.java | 59 +- .../BundleTypeInResponseHl7OrgTest.java | 52 +- .../server/CreateConditionalHl7OrgTest.java | 66 +- .../server/DeleteConditionalHl7OrgTest.java | 60 +- ...cludeAndRevincludeParameterHl7OrgTest.java | 56 +- .../rest/server/IncludeHl7OrgDstu2Test.java | 77 +- ...erationDuplicateServerHl7OrgDstu2Test.java | 58 +- .../server/OperationServerHl7OrgTest.java | 80 +- .../rest/server/PreferHl7OrgDstu2Test.java | 62 +- .../fhir/rest/server/ReadHl7OrgDstu2Test.java | 63 +- .../rest/server/SearchHl7OrgDstu2Test.java | 59 +- .../SearchWithGenericListHl7OrgDstu2Test.java | 57 +- .../SearchWithHl7OrgDstu2BundleTest.java | 63 +- ...verConformanceProviderHl7OrgDstu2Test.java | 8 +- ...ithBundleResourceParamHl7OrgDstu2Test.java | 4 +- .../UpdateConditionalHl7OrgDstu2Test.java | 66 +- .../rest/server/ValidateHl7OrgDstu2Test.java | 59 +- hapi-fhir-structures-r4/pom.xml | 69 +- ...stomThymeleafNarrativeGeneratorR4Test.java | 5 +- ...aultThymeleafNarrativeGeneratorR4Test.java | 2 +- .../ca/uhn/fhir/parser/RDFParserTest.java | 3 + .../fhir/rest/client/ClientHeadersR4Test.java | 10 +- .../rest/client/ClientIntegrationTest.java | 53 +- ...ompressOutgoingContentInterceptorTest.java | 54 +- .../uhn/fhir/rest/client/HttpProxyTest.java | 10 +- .../rest/client/LoggingInterceptorTest.java | 43 +- .../fhir/rest/param/DateRangeParamR4Test.java | 80 +- .../fhir/rest/server/BaseR4ServerTest.java | 51 +- .../fhir/rest/server/BinaryServerR4Test.java | 73 +- .../CapabilityStatementCacheR4Test.java | 2 +- .../ca/uhn/fhir/rest/server/CreateR4Test.java | 92 +- .../rest/server/DeleteConditionalR4Test.java | 52 +- .../fhir/rest/server/ETagServerR4Test.java | 70 +- .../fhir/rest/server/ElementsParamR4Test.java | 91 +- .../uhn/fhir/rest/server/HistoryR4Test.java | 71 +- .../ca/uhn/fhir/rest/server/IncludeTest.java | 70 +- .../fhir/rest/server/LastNProviderTest.java | 5 +- .../fhir/rest/server/MultitenancyR4Test.java | 73 +- .../server/OperationGenericServer2R4Test.java | 90 +- .../server/OperationGenericServerR4Test.java | 59 +- .../rest/server/OperationServerR4Test.java | 4 +- .../ca/uhn/fhir/rest/server/PagingTest.java | 2 +- .../server/PagingUsingNamedPagesR4Test.java | 78 +- .../rest/server/PatientResourceProvider.java | 2 +- .../fhir/rest/server/PlainProviderR4Test.java | 74 +- .../ca/uhn/fhir/rest/server/PreferTest.java | 69 +- ...onseCodeModifyingResourceProviderTest.java | 2 +- .../fhir/rest/server/RestfulServerTest.java | 4 +- .../SearchBundleProviderWithNoSizeR4Test.java | 48 +- .../rest/server/SearchHasParamR4Test.java | 49 +- .../ca/uhn/fhir/rest/server/SearchR4Test.java | 6 + .../rest/server/SearchSearchServerR4Test.java | 113 +-- .../fhir/rest/server/SearchSortR4Test.java | 52 +- .../rest/server/ServerConcurrencyTest.java | 8 +- .../server/ServerInvalidDefinitionR4Test.java | 21 +- .../server/ServerMethodSelectionR4Test.java | 18 +- .../rest/server/ServerMimetypeR4Test.java | 96 +- .../fhir/rest/server/SummaryParamR4Test.java | 69 +- .../interceptor/ConsentInterceptorTest.java | 9 +- .../ExceptionHandlingInterceptorTest.java | 4 +- .../ExceptionInterceptorMethodTest.java | 61 +- .../interceptor/InjectionAttackTest.java | 69 +- .../InterceptorThrowingExceptionR4Test.java | 68 +- ...> ResponseHighlighterInterceptorTest.java} | 181 ++-- .../ServeMediaResourceRawInterceptorTest.java | 73 +- ...eHighlightingInterceptorExceptionTest.java | 53 +- .../test/resources/junit-platform.properties | 2 + hapi-fhir-structures-r4b/pom.xml | 63 +- .../rest/server/PatientResourceProvider.java | 2 +- .../uhn/fhir/rest/server/SearchR4BTest.java | 48 +- ...verCapabilityStatementProviderR4BTest.java | 6 +- hapi-fhir-structures-r5/pom.xml | 63 +- .../rest/server/PatientResourceProvider.java | 2 +- .../ca/uhn/fhir/rest/server/SearchR5Test.java | 48 +- ...rverCapabilityStatementProviderR5Test.java | 6 +- hapi-fhir-test-utilities/pom.xml | 49 +- .../test/utilities/BaseRestServerHelper.java | 6 +- .../test/utilities/HttpClientExtension.java | 25 +- .../test/utilities/RestServerDstu3Helper.java | 2 +- .../test/utilities/RestServerR4Helper.java | 17 +- .../docker/DockerRequiredCondition.java | 30 +- .../jpa/JpaModelScannerAndVerifier.java | 53 +- .../server/BaseJettyServerExtension.java | 62 +- .../server/HttpServletExtension.java | 2 +- .../utilities/server/MockServletUtil.java | 2 +- .../server/RequestCaptureServlet.java | 113 +++ .../server/RestfulServerExtension.java | 17 +- hapi-fhir-testpage-overlay/pom.xml | 33 +- .../java/ca/uhn/fhir/to/BaseController.java | 4 +- .../main/java/ca/uhn/fhir/to/Controller.java | 4 +- .../ca/uhn/fhir/to/FhirTesterMvcConfig.java | 6 +- .../java/ca/uhn/fhir/to/TesterConfig.java | 4 +- .../to/client/BearerTokenClientFactory.java | 3 +- .../ca/uhn/fhir/to/model/HomeRequest.java | 11 +- .../uhn/fhir/to/mvc/ToBindingInitializer.java | 6 - .../uhn/fhir/jpa/test/FhirServerConfig.java | 2 +- .../ca/uhn/fhir/jpa/test/OverlayTestApp.java | 17 +- .../java/ca/uhn/fhir/jpa/test/WebTest.java | 33 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r4b/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 59 +- .../rest/server/PatientResourceProvider.java | 2 +- ...RequestValidatingInterceptorDstu3Test.java | 84 +- ...esponseValidatingInterceptorDstu3Test.java | 86 +- ...rverCapabilityStatementProviderR4Test.java | 4 +- .../auth/AuthorizationInterceptorR4Test.java | 797 ++++++++------- ...rminologyServiceValidationSupportTest.java | 2 +- ...logyServiceValidationSupportDstu3Test.java | 2 +- hapi-tinder-plugin/pom.xml | 26 +- .../tinder/ddl/DdlGeneratorHibernate61.java | 214 ++++ .../uhn/fhir/tinder/ddl/GenerateDdlMojo.java | 128 +++ .../resources/vm/jpa_resource_provider.vm | 4 +- hapi-tinder-test/pom.xml | 2 +- .../src/test/java/test/TestAntTask.java | 7 +- .../FhirInstanceValidatorTest.java | 2 - .../ResourceValidatorDstu3FeatureTest.java | 7 +- .../karaf/r4/R4JsonParserTest.java | 1 - pom.xml | 278 +++--- .../pom.xml | 13 +- .../example/ExtendedOrganizationResource.kt | 8 +- .../ExtendedOrganizationResourceTest.kt | 2 +- .../pom.xml | 4 +- .../pom.xml | 17 +- .../ca/uhn/fhir/testmindeps/ReadTest.java | 50 +- 876 files changed, 9407 insertions(+), 17801 deletions(-) rename hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/{BulkImportCommandIT.java => BulkImportCommandTest.java} (94%) rename {hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect => hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util}/ISequenceValueMassager.java (87%) delete mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java delete mode 100644 hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java create mode 100644 hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java rename hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/{SchedulerServiceImplIT.java => SchedulerServiceImplTest.java} (97%) delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java delete mode 100644 hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java rename hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/{ResponseHighlightingInterceptorTest.java => ResponseHighlighterInterceptorTest.java} (89%) create mode 100644 hapi-fhir-structures-r4/src/test/resources/junit-platform.properties create mode 100644 hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/server/RequestCaptureServlet.java create mode 100644 hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ddl/DdlGeneratorHibernate61.java create mode 100644 hapi-tinder-plugin/src/main/java/ca/uhn/fhir/tinder/ddl/GenerateDdlMojo.java diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index b901097f2db..7881deef30b 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -13,9 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; import java.util.Properties; public class MavenWrapperDownloader { diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 7828ab2b979..09f8ce921e9 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml @@ -45,7 +45,6 @@ false false false - false true true 2 @@ -103,14 +102,6 @@ net.bytebuddy byte-buddy
    - - javax.xml.bind - jaxb-api - - - com.sun.xml.bind - jaxb-impl - .*\.txt$ diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index c80236908a1..32004a48b33 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -131,12 +131,6 @@ org.jacoco jacoco-maven-plugin - - ${basedir}/target/classes - - - ${basedir}/src/main/java - true diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index ddb95cf5e41..7b126077940 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -52,19 +52,13 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api true - - - org.glassfish.jaxb - jaxb-core - - - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt true @@ -221,7 +215,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - ${maven_checkstyle_version} ${maven.multiModuleProjectDirectory}/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java index dc7074bba25..8c3bdcaa640 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 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.context.support; import java.util.Collection; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 64b7267f63f..a87023924be 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -158,10 +158,10 @@ public enum Pointcut implements IPointcut { * Hooks may accept the following parameters: *
      *
    • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
    • *
    • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
    • *
    *

    @@ -171,7 +171,7 @@ public enum Pointcut implements IPointcut { * no further processing will occur and no further interceptors will be called. */ SERVER_INCOMING_REQUEST_PRE_PROCESSED( - boolean.class, "javax.servlet.http.HttpServletRequest", "javax.servlet.http.HttpServletResponse"), + boolean.class, "jakarta.servlet.http.HttpServletRequest", "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -198,10 +198,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. * *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • *
  • * ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException - The exception that was thrown @@ -220,8 +220,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse", + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse", "ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException"), /** @@ -244,10 +244,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. *
  • *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -268,8 +268,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -291,10 +291,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -313,8 +313,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -416,10 +416,10 @@ public enum Pointcut implements IPointcut { * {@link NullPointerException} in the case of a bug being triggered. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -434,8 +434,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "java.lang.Throwable", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -463,10 +463,10 @@ public enum Pointcut implements IPointcut { * ca.uhn.fhir.rest.api.server.ResponseDetails - This object contains details about the response, including the contents. Hook methods may modify this object to change or replace the response. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -487,8 +487,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "org.hl7.fhir.instance.model.api.IBaseResource", "ca.uhn.fhir.rest.api.server.ResponseDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -554,10 +554,10 @@ public enum Pointcut implements IPointcut { * java.lang.String - The GraphQL response * *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -578,8 +578,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "java.lang.String", "java.lang.String", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java index 0b877f2c434..6e675c3350f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java @@ -24,7 +24,11 @@ import ca.uhn.fhir.model.api.annotation.Description; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public abstract class BaseElement implements /*IElement, */ ISupportsUndeclaredExtensions { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java index f506a1f57aa..0532423b8ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java @@ -19,10 +19,15 @@ */ package ca.uhn.fhir.model.api; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.io.InputStream; import java.util.Date; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java index 8152d66f19c..b8cb84adc4b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java @@ -41,9 +41,18 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; import java.util.function.Consumer; import java.util.stream.Collectors; -import java.util.*; import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java index 83907544fc5..69a521a77ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java @@ -118,7 +118,7 @@ public @interface Operation { * always the right choice), the framework will not attempt to generate a response to * this method. *

    - * This is useful if you want to include an {@link javax.servlet.http.HttpServletResponse} + * This is useful if you want to include an {@link jakarta.servlet.http.HttpServletResponse} * in your method parameters and create a response yourself directly from your * @Operation method. *

    @@ -134,7 +134,7 @@ public @interface Operation { * always the right choice), the framework will not attempt to parse the request body, * but will instead delegate it to the @Operation method. *

    - * This is useful if you want to include an {@link javax.servlet.http.HttpServletRequest} + * This is useful if you want to include an {@link jakarta.servlet.http.HttpServletRequest} * in your method parameters and parse the request yourself. *

    */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java index 4276229f630..aedd0656b36 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java @@ -23,7 +23,10 @@ import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IRestfulClient; import org.hl7.fhir.instance.model.api.IBaseResource; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * RESTful method annotation to be used for the FHIR
    read and transaction method. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java index 8e50b13c9bb..51539b5d384 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java @@ -19,7 +19,10 @@ */ package ca.uhn.fhir.rest.annotation; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java index 7826df8daee..2ece6309e97 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java @@ -22,7 +22,10 @@ package ca.uhn.fhir.rest.annotation; import ca.uhn.fhir.rest.api.ValidationModeEnum; import org.hl7.fhir.instance.model.api.IBaseResource; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * RESTful method annotation to be used for the FHIR diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java index 2f8a91f22f4..d51b427f271 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java @@ -23,7 +23,9 @@ import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseCoding; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java index 3763c5292ae..56910ea563b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java @@ -19,7 +19,9 @@ */ package ca.uhn.fhir.rest.param; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * Comparator/qualifier for values used in REST params, such as {@link DateParam}, {@link NumberParam}, and diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java index 9e7d82dd51d..cbb1adceb58 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java @@ -19,7 +19,11 @@ */ package ca.uhn.fhir.util; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.ICompositeType; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java index c1678236b7d..3f4cdc49df6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java @@ -24,7 +24,8 @@ import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.riot.Lang; import org.apache.jena.riot.RDFDataMgr; -import java.io.*; +import java.io.Reader; +import java.io.Writer; public class RDFUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java index 9355952873b..bd90fb8ca6a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java @@ -37,8 +37,8 @@ import com.helger.commons.io.resource.ClassPathResource; import com.helger.commons.io.resource.IReadableResource; import com.helger.schematron.ISchematronResource; import com.helger.schematron.SchematronHelper; +import com.helger.schematron.sch.SchematronResourceSCH; import com.helger.schematron.svrl.jaxb.SchematronOutputType; -import com.helger.schematron.xslt.SchematronResourceSCH; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -90,7 +90,12 @@ public class SchematronBaseValidator implements IValidatorModule { } StreamSource source = new StreamSource(new StringReader(resourceAsString)); - SchematronOutputType results = SchematronHelper.applySchematron(sch, source); + SchematronOutputType results; + try { + results = sch.applySchematronValidationToSVRL(source); + } catch (Exception e) { + throw new InternalErrorException(Msg.code(2433) + e.getMessage(), e); + } if (results == null) { return; } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java index 29857f00522..654d40aa675 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java @@ -1,12 +1,14 @@ package ca.uhn.fhir.i18n; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.jupiter.api.Test; import java.util.Set; -import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HapiLocalizerTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java index b0ec6d38352..70f56111cf9 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java @@ -2,7 +2,8 @@ package ca.uhn.fhir.model.api; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class ResourceMetadataKeyEnumTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java index ab747cf2923..2bfe43c07eb 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java @@ -5,7 +5,10 @@ import org.junit.jupiter.api.Test; import java.net.URI; import java.net.URISyntaxException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TagTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java index 744e03b17bd..0d7991d85d8 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java @@ -5,7 +5,9 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CacheControlDirectiveTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java index b510b658f69..2e2a27e7cbe 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.api; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EncodingEnumTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java index 89a29b045da..5049c53ebc0 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java @@ -7,7 +7,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class MethodOutcomeTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java index e1b13e4f6c2..1e89acc623c 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java @@ -17,7 +17,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; public class DateParamTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateParamTest.class); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java index 33eb930e129..f844bdb9dca 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java @@ -3,7 +3,8 @@ package ca.uhn.fhir.rest.param; import com.google.common.collect.Sets; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class QualifierDetailsTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java index 064443fcf5e..3b6f7609537 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java @@ -1,14 +1,11 @@ package ca.uhn.fhir.rest.param; -import static ca.uhn.fhir.rest.api.Constants.PARAMQUALIFIER_STRING_TEXT; -import static org.junit.jupiter.api.Assertions.*; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IQueryParameterType; import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -18,12 +15,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import ch.qos.logback.classic.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.stream.Collectors; +import static ca.uhn.fhir.rest.api.Constants.PARAMQUALIFIER_STRING_TEXT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + @ExtendWith(MockitoExtension.class) public class StringParamTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java index 78d38818858..ee0167d79ab 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server.exceptions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BaseServerResponseExceptionTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java index cce99a49e8b..fc75a7dff89 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -19,7 +18,9 @@ import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; class DateRangeUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java index 1f8fa0c6578..a1f566f436c 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java @@ -5,7 +5,11 @@ import org.slf4j.Logger; import org.slf4j.event.Level; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; public class LogUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java index 776f24109c8..9f5dbd46357 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java @@ -1,15 +1,17 @@ package ca.uhn.fhir.util; -import static org.junit.jupiter.api.Assertions.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.i18n.Msg; +import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import ca.uhn.fhir.i18n.Msg; -import org.junit.jupiter.api.Test; - -import ca.uhn.fhir.context.ConfigurationException; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class ReflectionUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java index 860c6367a6a..bc689c55259 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.validation; -import static org.junit.jupiter.api.Assertions.*; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ResultSeverityEnumTest { @Test diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 80805431db2..e70d734aa84 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 75efb8b7607..a1e8d7553cd 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml @@ -42,7 +42,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.0 com.puppycrawl.tools @@ -109,7 +108,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - ${maven_checkstyle_version} **/osgi/**/*, **/.mvn/**/*, **/.mvn_/**/* diff --git a/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml b/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml index 5ff02a4549d..620676e3c80 100644 --- a/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml +++ b/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml @@ -18,6 +18,13 @@ + + + diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 953d36a6f18..3126cd0baf1 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml @@ -152,38 +152,25 @@ spring-web + - org.eclipse.jetty - jetty-servlets + org.eclipse.jetty.ee10.websocket + jetty-ee10-websocket-jakarta-client - org.eclipse.jetty - jetty-servlet + jakarta.websocket + jakarta.websocket-client-api org.eclipse.jetty jetty-server - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-client + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + org.slf4j jcl-over-slf4j @@ -195,39 +182,17 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 - com.helger - ph-schematron - - - org.glassfish.jaxb - jaxb-runtime - - - org.glassfish.jaxb - jaxb-core - - - com.sun.istack - istack-commons-runtime - - + com.helger.schematron + ph-schematron-api - com.helger - ph-commons - - - javax.xml.bind - jaxb-api - - - org.glassfish.jaxb - jaxb-runtime + com.helger.schematron + ph-schematron-xslt @@ -272,6 +237,15 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + none + false + 1 + + diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java index 8794891e7f3..3b8868eeaa9 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java @@ -40,11 +40,12 @@ import org.apache.commons.io.file.PathUtils; import org.apache.commons.io.filefilter.FileFileFilter; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter; +import org.apache.commons.lang3.time.DateUtils; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -234,7 +235,12 @@ public class BulkImportCommand extends BaseCommand { private List startServer(int thePort, List files) { List indexes = new ArrayList<>(); - myServer = new Server(thePort); + + myServer = new Server(); + ServerConnector connector = new ServerConnector(myServer); + connector.setIdleTimeout(DateUtils.MILLIS_PER_MINUTE); + connector.setPort(myPort); + myServer.setConnectors(new Connector[] {connector}); myServlet = new BulkImportFileServlet(); for (File t : files) { diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java index 36524c393da..f5ea9c1f8dd 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.util.ParametersUtil; -import joptsimple.internal.Strings; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.ParseException; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -111,7 +110,7 @@ public class ReindexTerminologyCommand extends BaseRequestGeneratingCommand { @Nonnull private String getResponseMessage(IBaseParameters response) { List message = ParametersUtil.getNamedParameterValuesAsString(myFhirCtx, response, "message"); - return Strings.join(message, NL); + return String.join(NL, message); } public static final String NL = System.getProperty("line.separator"); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java index 8ec137c69b7..1a13173f9f3 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java @@ -21,19 +21,17 @@ package ca.uhn.fhir.cli; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.primitive.IdDt; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.ContainerProvider; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.WebSocketContainer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.eclipse.jetty.websocket.api.Frame; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; import java.net.URI; @@ -46,6 +44,7 @@ public class WebsocketSubscribeCommand extends BaseCommand { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebsocketSubscribeCommand.class); private boolean myQuit; + private Session mySession; @Override public String getCommandDescription() { @@ -75,14 +74,13 @@ public class WebsocketSubscribeCommand extends BaseCommand { IdDt subsId = new IdDt(theCommandLine.getOptionValue("i")); - WebSocketClient client = new WebSocketClient(); SimpleEchoSocket socket = new SimpleEchoSocket(subsId.getIdPart()); try { - client.start(); URI echoUri = new URI(target); - ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); - client.connect(socket, echoUri, request); + + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + mySession = container.connectToServer(socket, echoUri); while (!myQuit) { Thread.sleep(500L); @@ -93,7 +91,9 @@ public class WebsocketSubscribeCommand extends BaseCommand { throw new CommandFailureException(Msg.code(1537) + e); } finally { try { - client.stop(); + if (mySession != null) { + mySession.close(); + } } catch (Exception e) { ourLog.error("Failure", e); } @@ -103,7 +103,7 @@ public class WebsocketSubscribeCommand extends BaseCommand { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @ClientEndpoint public class SimpleEchoSocket { private String mySubsId; @@ -115,37 +115,32 @@ public class WebsocketSubscribeCommand extends BaseCommand { mySubsId = theSubsId; } - @OnWebSocketClose + @OnClose public void onClose(int statusCode, String reason) { ourLog.info("Received CLOSE status={} reason={}", statusCode, reason); } - @OnWebSocketConnect + @OnOpen public void onConnect(Session theSession) { ourLog.info("Successfully connected"); this.session = theSession; try { String sending = "bind " + mySubsId; LOG_SEND.info("{}", sending); - theSession.getRemote().sendString(sending); + theSession.getBasicRemote().sendText(sending); } catch (Throwable t) { ourLog.error("Failure", t); myQuit = true; } } - @OnWebSocketError + @OnError public void onError(Throwable theError) { ourLog.error("Websocket error: ", theError); myQuit = true; } - @OnWebSocketFrame - public void onFrame(Frame theFrame) { - ourLog.debug("Websocket frame: {}", theFrame); - } - - @OnWebSocketMessage + @OnMessage public void onMessage(String theMsg) { LOG_RECV.info("{}", theMsg); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java similarity index 94% rename from hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java rename to hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java index c7f4369faef..db1ea7036ab 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java @@ -54,9 +54,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class BulkImportCommandIT { +public class BulkImportCommandTest { - private static final Logger ourLog = LoggerFactory.getLogger(BulkImportCommandIT.class); + private static final Logger ourLog = LoggerFactory.getLogger(BulkImportCommandTest.class); static { HapiSystemProperties.enableTestMode(); @@ -64,7 +64,7 @@ public class BulkImportCommandIT { @RegisterExtension public HttpClientExtension myHttpClientExtension = new HttpClientExtension(); - @Mock + @Mock(strictness = Mock.Strictness.LENIENT) private IJobCoordinator myJobCoordinator; private final BulkDataImportProvider myProvider = new BulkDataImportProvider(); private final FhirContext myCtx = FhirContext.forR4Cached(); @@ -101,19 +101,12 @@ public class BulkImportCommandIT { @Test public void testBulkImport() throws IOException { - JobInstance jobInfo = new JobInstance() - .setStatus(StatusEnum.COMPLETED) - .setCreateTime(parseDate("2022-01-01T12:00:00-04:00")) - .setStartTime(parseDate("2022-01-01T12:10:00-04:00")); - - when(myJobCoordinator.getInstance(eq("THE-JOB-ID"))).thenReturn(jobInfo); - String fileContents1 = "{\"resourceType\":\"Observation\"}\n{\"resourceType\":\"Observation\"}"; String fileContents2 = "{\"resourceType\":\"Patient\"}\n{\"resourceType\":\"Patient\"}"; writeNdJsonFileToTempDirectory(fileContents1, "file1.json"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json"); - when(myJobCoordinator.startInstance(any())).thenReturn(createJobStartResponse("THE-JOB-ID")); + when(myJobCoordinator.startInstance(any(), any())).thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread new Thread(() -> App.main(new String[]{ @@ -154,7 +147,7 @@ public class BulkImportCommandIT { writeNdJsonFileToTempDirectory(fileContents1, "file1.json.gz"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json.gz"); - when(myJobCoordinator.startInstance(any())) + when(myJobCoordinator.startInstance(any(), any())) .thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread @@ -196,7 +189,7 @@ public class BulkImportCommandIT { writeNdJsonFileToTempDirectory(fileContents1, "file1.json"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json"); - when(myJobCoordinator.startInstance(any())).thenReturn(createJobStartResponse("THE-JOB-ID")); + when(myJobCoordinator.startInstance(any(), any())).thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread new Thread(() -> App.main(new String[]{ diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java index 635ce997c65..d6cd7dc01dd 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java @@ -21,14 +21,19 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; import static ca.uhn.fhir.jpa.migrate.HapiMigrationLock.LOCK_PID; import static ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc.LOCK_TYPE; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.slf4j.LoggerFactory.getLogger; public class HapiClearMigrationLockCommandTest extends ConsoleOutputCapturingBaseTest { diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java index 31b74d0b5ab..7c9601db73f 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java @@ -258,8 +258,9 @@ public class HapiFlywayMigrateDatabaseCommandTest { private File getLocation(String theDatabaseName) throws IOException { File directory = new File(myDbDirectory); if (directory.exists()) { - FileUtils.deleteDirectory(directory); + FileUtils.forceDelete(directory); } + assertFalse(directory.exists()); return new File(myDbDirectory + "/" + theDatabaseName); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java index 38cbd22c317..cc79a07c4a0 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java @@ -8,16 +8,19 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.ParseException; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; @@ -46,33 +49,29 @@ public class HeaderPassthroughOptionTest { private static final Logger ourLog = LoggerFactory.getLogger(HeaderPassthroughOptionTest.class); final String FHIR_VERSION = "r4"; - private FhirContext myCtx = FhirContext.forR4(); - private Server myServer; - private int myPort; + private final FhirContext myCtx = FhirContext.forR4Cached(); private final String headerKey1 = "test-header-key-1"; private final String headerValue1 = "test header value-1"; private static final String ourConceptsFileName = "target/concepts.csv"; private static final String ourHierarchyFileName = "target/hierarchy.csv"; + private static final AtomicInteger ourFilenameCounter = new AtomicInteger(); private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); private final UploadTerminologyCommand testedCommand = new RequestCapturingUploadTerminologyCommand(myCapturingInterceptor); + private final TerminologyUploaderProvider myProvider = new TerminologyUploaderProvider(); @Mock protected ITermLoaderSvc myTermLoaderSvc; - private static final AtomicInteger ourFilenameCounter = new AtomicInteger(); + + @RegisterExtension + public RestfulServerExtension myServer = new RestfulServerExtension(myCtx) + .registerProvider(myProvider); @BeforeEach - public void beforeEach() throws Exception { - myServer = new Server(0); - TerminologyUploaderProvider provider = new TerminologyUploaderProvider(myCtx, myTermLoaderSvc); - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(provider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + public void beforeEach() { + myProvider.setContext(myCtx); + myProvider.setTerminologyLoaderSvc(myTermLoaderSvc); + when(myTermLoaderSvc.loadCustom(eq("http://foo"), anyList(), any())) .thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101"))); } @@ -85,7 +84,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), @@ -115,7 +114,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), @@ -149,7 +148,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index dd37903542f..ec740677b09 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index b2fe5bc90b3..19562217626 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 70061eae755..70ac8a73a06 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -81,11 +81,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - org.jboss.resteasy resteasy-client diff --git a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java index 1969b94293d..0c38e94d735 100644 --- a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java +++ b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java @@ -28,39 +28,27 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.io.Charsets; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.io.IOException; import java.util.Date; -import java.util.Enumeration; import java.util.List; -import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -76,23 +64,14 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericOkHttpClientDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericOkHttpClientDstu2Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + + private static final RequestCaptureServlet MY_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(MY_SERVLET) + .keepAliveBetweenTests(); /** * This suite of tests can be reconfigured to test a different RestfulClientFactory implementation by @@ -115,9 +94,10 @@ public class GenericOkHttpClientDstu2Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + MY_SERVLET.reset(); } + @Test public void testProviderWhereWeForgotToSetTheContext() throws Exception { RestfulClientFactory clientFactory = createNewClientFactoryForTesting(null); @@ -126,7 +106,7 @@ public class GenericOkHttpClientDstu2Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { String factoryClassName = clientFactory.getClass().getSimpleName(); @@ -163,25 +143,25 @@ public class GenericOkHttpClientDstu2Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @Test @@ -194,24 +174,24 @@ public class GenericOkHttpClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -224,23 +204,23 @@ public class GenericOkHttpClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -253,78 +233,78 @@ public class GenericOkHttpClientDstu2Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Conformance resp = (Conformance) client.capabilities().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @Test public void testCreate() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - String body = ourRequestBodyString; + String body = MY_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @Test public void testCreateConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } private void assertContentTypeEquals(String expectedContentTypeHeader) { @@ -335,20 +315,20 @@ public class GenericOkHttpClientDstu2Test { @Test public void testCreatePrefer() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @Test @@ -357,11 +337,11 @@ public class GenericOkHttpClientDstu2Test { p.setId("123"); final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -374,33 +354,33 @@ public class GenericOkHttpClientDstu2Test { @Test public void testDeleteConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); } @SuppressWarnings("deprecation") @Test public void testDeleteNonFluent() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); } @@ -408,10 +388,10 @@ public class GenericOkHttpClientDstu2Test { public void testHistory() throws Exception { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -421,7 +401,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -432,7 +412,7 @@ public class GenericOkHttpClientDstu2Test { .count(null) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -442,7 +422,7 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt()) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -451,7 +431,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -460,7 +440,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -471,8 +451,8 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z")) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); response = client @@ -482,7 +462,7 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) .execute(); - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -496,10 +476,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client .meta() @@ -509,11 +489,11 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals("", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); } @Test @@ -527,10 +507,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client .meta() @@ -538,9 +518,9 @@ public class GenericOkHttpClientDstu2Test { .fromServer() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .meta() @@ -548,9 +528,9 @@ public class GenericOkHttpClientDstu2Test { .fromType("Patient") .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .meta() @@ -558,9 +538,9 @@ public class GenericOkHttpClientDstu2Test { .fromResource(new IdDt("Patient/123")) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @Test @@ -577,10 +557,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -590,9 +570,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -602,9 +582,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -614,9 +594,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -625,7 +605,7 @@ public class GenericOkHttpClientDstu2Test { .withParameters(inParams) .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); } @Test @@ -637,10 +617,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -650,9 +630,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -662,9 +642,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -674,9 +654,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -685,17 +665,17 @@ public class GenericOkHttpClientDstu2Test { .withNoParameters(Parameters.class) .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() throws Exception { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; - ourResponseBody = respString; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -731,10 +711,10 @@ public class GenericOkHttpClientDstu2Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -742,11 +722,11 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp.getParameter().get(0).getResource().getClass()); } @@ -760,10 +740,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -774,14 +754,14 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* * Composite type @@ -796,14 +776,14 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* * Resource @@ -818,19 +798,19 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -887,10 +867,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client .operation() @@ -901,7 +881,7 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", MY_SERVLET.ourRequestUri); client .operation() @@ -912,11 +892,11 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", MY_SERVLET.ourRequestUri); + ourLog.info(MY_SERVLET.ourRequestBodyString); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); } @Test @@ -933,10 +913,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -944,12 +924,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -957,12 +937,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -970,16 +950,16 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test @@ -994,10 +974,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -1005,12 +985,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1018,12 +998,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1031,12 +1011,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1045,19 +1025,19 @@ public class GenericOkHttpClientDstu2Test { .withNoParameters(Parameters.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testPageNext() throws Exception { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client .loadPage() @@ -1065,12 +1045,12 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", MY_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); try { @@ -1082,13 +1062,13 @@ public class GenericOkHttpClientDstu2Test { @Test public void testPagePrev() throws Exception { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client .loadPage() @@ -1096,14 +1076,14 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); /* * Try with "prev" instead of "previous" */ sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); resp = client .loadPage() @@ -1111,7 +1091,7 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); } @Test @@ -1120,15 +1100,15 @@ public class GenericOkHttpClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1138,15 +1118,15 @@ public class GenericOkHttpClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1167,10 +1147,10 @@ public class GenericOkHttpClientDstu2Test { " \n" + ""; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -1187,10 +1167,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithElementsParam() throws Exception { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); IBaseResource response = client.read() .resource("Patient") @@ -1198,8 +1178,8 @@ public class GenericOkHttpClientDstu2Test { .elementsSubset("name", "identifier") .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1207,10 +1187,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithSummaryInvalid() throws Exception { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); try { client.read() @@ -1229,10 +1209,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithSummaryParamHtml() throws Exception { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response = client.read() .resource(Patient.class) @@ -1240,7 +1220,7 @@ public class GenericOkHttpClientDstu2Test { .summaryMode(SummaryEnum.TEXT) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); } @@ -1249,10 +1229,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByString() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1260,7 +1240,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1268,18 +1248,18 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByUrl() throws Exception { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1288,7 +1268,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1297,7 +1277,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1305,7 +1285,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1313,7 +1293,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); try { @@ -1330,10 +1310,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchReturningDstu2Bundle() throws Exception { String msg = IOUtils.toString(GenericOkHttpClientDstu2Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() .forResource("Observation") @@ -1355,10 +1335,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchWithElementsParam() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1367,8 +1347,8 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1376,10 +1356,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByPost() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1389,29 +1369,29 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", MY_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + // "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(MY_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test public void testSearchByPostUseJson() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1422,30 +1402,30 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); - assertThat(ourRequestUri, not(containsString("_format=json"))); + assertThat(MY_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(MY_SERVLET.ourRequestUri, not(containsString("_format=json"))); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + // "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test - public void testSearchWithLastUpdated() throws Exception { + public void testSearchWithLastUpdated() { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1454,18 +1434,18 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @Test - public void testSearchWithProfileAndSecurity() throws Exception { + public void testSearchWithProfileAndSecurity() { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1476,7 +1456,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1485,10 +1465,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchWithReverseInclude() throws Exception { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource(Patient.class) @@ -1497,17 +1477,17 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", MY_SERVLET.ourRequestUri); } @Test public void testSearchWithSummaryParam() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1516,7 +1496,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1535,16 +1515,16 @@ public class GenericOkHttpClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); String response = client.transaction() .withBundle(reqString) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); assertContentTypeEquals("application/json+fhir; charset=UTF-8"); @@ -1553,7 +1533,7 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertContentTypeEquals("application/xml+fhir; charset=UTF-8"); } @@ -1564,10 +1544,10 @@ public class GenericOkHttpClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle input = new ca.uhn.fhir.model.dstu2.resource.Bundle(); @@ -1585,7 +1565,7 @@ public class GenericOkHttpClientDstu2Test { .encodedJson() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1594,97 +1574,97 @@ public class GenericOkHttpClientDstu2Test { @Test public void testUpdateConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); } private String getActualContentTypeHeader() { - return ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"); + return MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"); } @Test public void testUpdateNonFluent() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update(new IdDt("Patient/123"), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); } @Test public void testUpdatePrefer() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.setId(new IdDt("1")); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @Test @@ -1693,11 +1673,11 @@ public class GenericOkHttpClientDstu2Test { p.setId("123"); final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -1714,10 +1694,10 @@ public class GenericOkHttpClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -1725,34 +1705,34 @@ public class GenericOkHttpClientDstu2Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); } @@ -1763,10 +1743,10 @@ public class GenericOkHttpClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -1776,11 +1756,11 @@ public class GenericOkHttpClientDstu2Test { response = client.validate(p); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); } @@ -1789,77 +1769,4 @@ public class GenericOkHttpClientDstu2Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException, ServletException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 1f8a6b2d2f6..bed73ac4cbf 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java index 4235ef0de4a..4269b074d22 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java @@ -22,19 +22,29 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RequestTypeEnum; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import java.util.*; /** * A Http Client based on Apache. This is an adapter around the class diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java index 33c573d7054..2fb0546f33e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java @@ -26,14 +26,24 @@ import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.StopWatch; import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.entity.ContentType; -import org.apache.http.*; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * A Http Response based on Apache. This is an adapter around the class diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java index 79fc6d71461..e6c0cdea08c 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.HttpClientUtil; import ca.uhn.fhir.rest.client.api.IHttpClient; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java index ce6869ec4fd..06e974f6c41 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import org.apache.http.Header; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.client.methods.HttpRequestBase; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java index 6cb8d3c1dfa..2e86a893fdb 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java index 923680eaabc..4f22f76470d 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; /** * HTTP interceptor to be used for adding Cookie to requests. diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java index 0e8e1c33a6b..11c50a18655 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java @@ -19,16 +19,41 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.AddTags; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.DeleteTags; +import ca.uhn.fhir.rest.annotation.GetPage; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ReflectionUtil; import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -37,7 +62,10 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; public abstract class BaseMethodBinding implements IClientResponseHandler { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java index 91db00c0dbd..c2043d7e9dd 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java @@ -32,7 +32,9 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Set; abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java index a31703842ab..af1b00f22b9 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java @@ -19,9 +19,12 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.param.ParameterUtil; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java index 37ce4bdf3eb..12a479b9e72 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java @@ -29,7 +29,9 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; class ConditionalParamBinder implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java index a69d57f5c62..6e63d2a368e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java @@ -31,7 +31,10 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; public class DeleteMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java index 86997ad19e1..38580994c73 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java @@ -31,7 +31,9 @@ import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java index d80cc262a19..2fff425f0ac 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java @@ -24,7 +24,9 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; class NullParameter implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java index b852238c9bc..fb723b1849d 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java @@ -31,7 +31,13 @@ import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.ParametersUtil; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java index fd53b07ae4f..cee15f46595 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java @@ -24,7 +24,9 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.Patch; import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.PatchTypeEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -33,7 +35,12 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; /** * Base class for an operation that has a resource type but not a resource body in the diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java index eb147489828..8b919104632 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java @@ -26,7 +26,9 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class RawParamsParmeter implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java index d2710f86fe4..014cce3e569 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java @@ -24,18 +24,25 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseBinary; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class ReadMethodBinding extends BaseResourceReturningMethodBinding implements IClientResponseHandlerHandlesBinary { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java index 058eda8f287..4fcecab24d8 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java @@ -19,16 +19,56 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IQueryParameterAnd; +import ca.uhn.fhir.model.api.IQueryParameterOr; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.param.binder.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.param.CompositeAndListParam; +import ca.uhn.fhir.rest.param.CompositeOrListParam; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateAndListParam; +import ca.uhn.fhir.rest.param.DateOrListParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.HasAndListParam; +import ca.uhn.fhir.rest.param.HasOrListParam; +import ca.uhn.fhir.rest.param.HasParam; +import ca.uhn.fhir.rest.param.NumberAndListParam; +import ca.uhn.fhir.rest.param.NumberOrListParam; +import ca.uhn.fhir.rest.param.NumberParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriAndListParam; +import ca.uhn.fhir.rest.param.UriOrListParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.param.binder.CalendarBinder; +import ca.uhn.fhir.rest.param.binder.DateBinder; +import ca.uhn.fhir.rest.param.binder.FhirPrimitiveBinder; +import ca.uhn.fhir.rest.param.binder.IParamBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterOrBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterTypeBinder; +import ca.uhn.fhir.rest.param.binder.StringBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.CollectionUtil; import ca.uhn.fhir.util.ReflectionUtil; @@ -36,7 +76,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class SearchParameter extends BaseQueryParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java index 546fa2bdff2..e793c0361a6 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java @@ -19,7 +19,9 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.annotation.TransactionParam; @@ -28,7 +30,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class TransactionParameter implements IParameter { diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index bcaa65bb8cb..0accae54174 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -33,8 +33,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -126,36 +126,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ca.uhn.hapi.fhir hapi-fhir-test-utilities @@ -183,7 +153,7 @@ ca.uhn.fhir.rest.server*;resolution:=optional, - javax.servlet.http;resolution:=optional, + jakarta.servlet.http;resolution:=optional, * diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java index c009350abc5..6de828b406f 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationConstants; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.converter.NullVersionConverterAdvisor10_30; import org.hl7.fhir.converter.NullVersionConverterAdvisor10_40; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_30; @@ -41,8 +43,6 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.StringTokenizer; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java index 08e6f17231c..7d83cabfe68 100644 --- a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java +++ b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -15,14 +17,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -35,16 +38,21 @@ import static org.hamcrest.MatcherAssert.assertThat; public class VersionedApiConverterInterceptorR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(VersionedApiConverterInterceptorR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new VersionedApiConverterInterceptor()) + .setDefaultResponseEncoding(EncodingEnum.JSON); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -54,7 +62,7 @@ public class VersionedApiConverterInterceptorR4Test { @Test public void testSearchConvertToR2() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("Accept", "application/fhir+json; fhirVersion=1.0"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -65,7 +73,7 @@ public class VersionedApiConverterInterceptorR4Test { @Test public void testSearchConvertToR2ByFormatParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + UrlUtil.escapeUrlParam("application/fhir+json; fhirVersion=1.0")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + UrlUtil.escapeUrlParam("application/fhir+json; fhirVersion=1.0")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -75,36 +83,9 @@ public class VersionedApiConverterInterceptorR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setDefaultPrettyPrint(true); - servlet.registerInterceptor(new VersionedApiConverterInterceptor()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index bd14759edc9..298abf36b56 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml @@ -249,18 +249,12 @@ thymeleaf - com.helger - ph-schematron - - - Saxon-HE - net.sf.saxon - - + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.springframework diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index ce9d6c844ce..0de0b3b0a0a 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -72,10 +72,6 @@ jackson-dataformat-yaml - - javax.ejb - ejb-api - javax.ws.rs jsr311-api @@ -104,8 +100,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java index 94a2becaeac..ae017dc82d4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java @@ -35,7 +35,14 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.interceptor.auth.*; +import ca.uhn.fhir.rest.server.interceptor.auth.AdditionalCompartmentSearchParameters; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizedList; +import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; +import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; +import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingConsentService; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor; import ca.uhn.fhir.rest.server.interceptor.consent.RuleFilteringConsentService; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java index f40e1a0ef6e..4a284f8f03e 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java @@ -23,8 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.server.util.ITestingUiClientFactory; - -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; public class AuthorizingTesterUiClientFactory implements ITestingUiClientFactory { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java index f04b586d38f..94e987a1c0c 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java @@ -26,11 +26,11 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.storage.interceptor.balp.AsyncMemoryQueueBackedFhirClientBalpSink; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditContextServices; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditEventSink; +import jakarta.servlet.ServletException; import org.hl7.fhir.r4.model.Reference; import java.util.List; import javax.annotation.Nonnull; -import javax.servlet.ServletException; public class BalpExample { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java index cbd954ce231..de96c72ec61 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java @@ -21,7 +21,13 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.Reference; public class ClientTransactionExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java index 35594752f37..c16556aedb4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java @@ -29,7 +29,11 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java index 3ec9e5275b6..7de0d0ff5f1 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java @@ -24,9 +24,9 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import jakarta.servlet.ServletException; import java.util.Collection; -import javax.servlet.ServletException; @SuppressWarnings("serial") public class Dstu2Examples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java index d2dcb9a74f8..703dabe9c65 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java @@ -20,7 +20,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java index 601a94cb353..9d7fa02fea7 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java @@ -21,11 +21,11 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; import java.util.ArrayList; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; // START SNIPPET: servlet diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java index a433b2f27d6..267ea3c7249 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java @@ -22,7 +22,14 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; import java.io.IOException; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java index 786e1ffed14..58daa9517e3 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java @@ -22,7 +22,11 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; public class FhirContextIntro { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java index 4a365cd0597..37ebe099e66 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java @@ -22,7 +22,17 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.InstantType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.SimpleQuantity; +import org.hl7.fhir.r4.model.StringType; import java.util.Date; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java index 8827962da1e..3312ba27702 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java @@ -22,13 +22,13 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; +import jakarta.ejb.EJB; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import java.util.concurrent.ConcurrentHashMap; -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; /** * Conformance Rest Service diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java index 90981c02318..3130b526342 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java @@ -21,22 +21,30 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import jakarta.ejb.Local; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; -import javax.ejb.Local; -import javax.ejb.Stateless; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; - /** * A demo JaxRs Patient Rest Provider */ diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java index dbe13655830..d17a23f2afd 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.springframework.beans.factory.annotation.Autowired; import java.util.Set; -import javax.servlet.http.HttpServletRequest; @SuppressWarnings("InnerClassMayBeStatic") public class PartitionExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java index be64bc050ba..ca352849127 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java @@ -22,9 +22,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; // START SNIPPET: interceptor @Interceptor diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java index 4e89f7eb9b9..c9cffa52427 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java @@ -23,10 +23,10 @@ import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; // START SNIPPET: interceptor public class RequestExceptionInterceptor { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java index a1c27f0d831..2d30a3ec2d4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java @@ -27,19 +27,70 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.parser.DataFormatException; +import ca.uhn.fhir.rest.annotation.At; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Count; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.IncludeParam; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.Offset; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Since; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.TransactionParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.SortOrderEnum; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.client.api.IBasicClient; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.CapabilityStatement; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.DiagnosticReport; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier.IdentifierUse; +import org.hl7.fhir.r4.model.InstantType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; import java.io.IOException; import java.math.BigDecimal; @@ -47,8 +98,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; @SuppressWarnings("unused") public abstract class RestfulPatientResourceProviderMore implements IResourceProvider { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java index f44f6004ecf..ac81b7322af 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java @@ -25,11 +25,10 @@ import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - public class SecurityInterceptors { public void basicAuthInterceptorRealm() { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java index 6c78c25e8f9..0e8977d4346 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java @@ -21,9 +21,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.RestfulServer; - -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; @SuppressWarnings("serial") public class ServerETagExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java index 1365d2d61ec..173887ef082 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java @@ -24,7 +24,13 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.r4.model.Bundle; @@ -36,8 +42,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class ServerOperations { private static final Logger ourLog = LoggerFactory.getLogger(ServerOperations.class); diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java index a6b336f3cfa..7eb61d0711b 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java @@ -37,14 +37,14 @@ import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.SearchPreferHandlingInterceptor; import ca.uhn.fhir.rest.server.interceptor.StaticCapabilityStatementInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.Enumerations; import org.springframework.web.cors.CorsConfiguration; import java.util.Arrays; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; @SuppressWarnings({"serial", "RedundantThrows", "InnerClassMayBeStatic"}) public class ServletExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java index d75bd9d5064..7fa54099747 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.validation.SchemaBaseValidator; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.schematron.SchematronBaseValidator; +import jakarta.servlet.ServletException; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; @@ -60,7 +61,6 @@ import java.io.File; import java.io.FileReader; import java.util.List; import javax.annotation.Nonnull; -import javax.servlet.ServletException; @SuppressWarnings({"serial", "unused"}) public class ValidatorExamples { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml index e69de29bb2d..891e4962f07 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml @@ -0,0 +1,25 @@ +--- +- item: + type: "add" + title: "The version of a few dependencies have been bumped to the latest versions + (dependent HAPI modules listed in brackets): +
      +
    • Spring Framework (JPA): 5.3.27 -> 6.0.12
    • +
    • Spring Data BOM (JPA): 2021.2.2 -> 2023.0.0
    • +
    • Hibernate (JPA): 5.6.15.Final -> 6.2.8.Final
    • +
    • Hibernate Validator (JPA): 6.1.5.Final -> 8.0.0.Final
    • +
    • Hibernate Search (JPA): 6.1.6.Final -> 6.2.1.Final
    • +
    • Spring Boot (Boot+Starter): 2.7.12 -> 3.1.4
    • +
    • Jetty (CLI): 10.0.14 -> 12.0.2
    • +
    • Phloc Schematron (Schematron Validator): 5.6.5 -> 7.1.2
    • +
    • RestEasy (JAX-RS Server): 5.0.2.Final -> 6.2.5.Final
    • +
    " +- item: + type: "change" + title: "HAPI FHIR JPA now requires PostgreSQL 10+. Previously Postgres 9.4 + was supported but this version has been dropped." +- item: + type: "remove" + title: "The legacy `$lastn` support has been removed, and only the redesigned 'advanced' version is now + supported. The advanced version requires Elasticsearch." + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md index 5b5a473f744..763682c55ff 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md @@ -1,19 +1,20 @@ # Database Support -HAPI FHIR JPA Server maintains active support for several databases: +HAPI FHIR JPA Server maintains active support for several databases. -- [MS SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-2019) -- [PostgreSQL](https://www.postgresql.org/) -- [Oracle](https://www.oracle.com/ca-en/database/12c-database/) +The supported databases are regularly tested for ongoing compliance and performance, and HAPI FHIR has specific performance optimizations for each platform. Make sure to use the HAPI FHIR dialect class as opposed to the default hibernate dialect class. -Use of any of the above databases is fully supported by HAPI-FHIR, and code is actively written to work with them. +| Database | Status | Hibernate Dialect Class | Notes | +|-----------------------------------------------------------------------------|---------------|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| [MS SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-2019) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect` | | +| [PostgreSQL](https://www.postgresql.org/) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect` | | +| [Oracle](https://www.oracle.com/ca-en/database/12c-database/) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect` | | +| [Cockroach DB](https://www.cockroachlabs.com/) | Experimental | `ca.uhn.fhir.jpa.model.dialect.HapiFhirCockroachDialect` | A CockroachDB dialect was contributed by a HAPI FHIR community member. This dialect is not regularly tested, use with caution. | +| MySQL | Deprecated | `ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect` | MySQL and MariaDB exhibit poor performance with HAPI FHIR and have therefore been deprecated. These databases should not be used. | +| MariaDB | Deprecated | `ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect` | MySQL and MariaDB exhibit poor performance with HAPI FHIR and have therefore been deprecated. These databases should not be used. | # Experimental Support -HAPI FHIR currently provides experimental for the following databases, but does not actively support them, or write code specifically to work with them: - -- [Cockroach DB](https://www.cockroachlabs.com/) - HAPI FHIR uses the Hibernate ORM to provide database abstraction. This means that HAPI FHIR could theoretically also work on other databases supported by Hibernate. For example, although we do not regularly test or validate on other platforms, community members have reported successfully running HAPI FHIR on: @@ -21,9 +22,3 @@ For example, although we do not regularly test or validate on other platforms, c - Cache - Firebird -# Deprecated Support - -These databases were previously supported by HAPI FHIR JPA Server, but have since been deprecated, and should not be used. - -- [MySQL](https://www.mysql.com/) - diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 47c0a8dc304..a1ae5cb4b1e 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index e4b20f53161..8b55c6e27ac 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -74,20 +74,22 @@
    - javax.ejb - ejb-api - provided + jakarta.ejb + jakarta.ejb-api - javax.servlet - javax.servlet-api - provided + jakarta.servlet + jakarta.servlet-api - org.jboss.spec.javax.ws.rs - jboss-jaxrs-api_2.1_spec - provided + jakarta.ws.rs + jakarta.ws.rs-api + + jakarta.interceptor + jakarta.interceptor-api + + org.springframework spring-context @@ -109,11 +111,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - ca.uhn.hapi.fhir hapi-fhir-test-utilities diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java index 8ea09b92709..5e1e2569665 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java @@ -29,20 +29,20 @@ import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; import ca.uhn.fhir.rest.client.method.MethodUtil; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation.Builder; +import jakarta.ws.rs.core.Form; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; import org.hl7.fhir.instance.model.api.IBaseBinary; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation.Builder; -import javax.ws.rs.core.Form; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; /** * A Http Request based on JaxRs. This is an adapter around the class - * {@link javax.ws.rs.client.Client Client} + * {@link jakarta.ws.rs.client.Client Client} * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java index 94f03508eed..844e46b81cf 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java @@ -25,19 +25,19 @@ import ca.uhn.fhir.rest.client.api.BaseHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.util.StopWatch; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.core.Response; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.core.Response; /** * A Http Request based on JaxRs. This is an adapter around the class - * {@link javax.ws.rs.client.Invocation Invocation} + * {@link jakarta.ws.rs.client.Invocation Invocation} * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java index 1522d5007d3..4599ee9e1b3 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java @@ -22,17 +22,17 @@ package ca.uhn.fhir.jaxrs.client; import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; import ca.uhn.fhir.util.StopWatch; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.*; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** - * A Http Response based on JaxRs. This is an adapter around the class {@link javax.ws.rs.core.Response Response} + * A Http Response based on JaxRs. This is an adapter around the class {@link jakarta.ws.rs.core.Response Response} * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ public class JaxRsHttpResponse extends BaseHttpResponse implements IHttpResponse { diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java index 68084615474..fc5788fa903 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java @@ -25,11 +25,11 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; /** * A Restful Client Factory, based on Jax Rs @@ -97,7 +97,7 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory { } /** - * Only accept clients of type javax.ws.rs.client.Client + * Only accept clients of type jakarta.ws.rs.client.Client * Can be used to set a specific Client implementation * @param theHttpClient */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java index fc00b175fd6..27fc15d6f9f 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java @@ -25,21 +25,25 @@ import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IRestfulServer; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; import java.util.Collections; import java.util.List; -import javax.interceptor.Interceptors; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; /** * This server is the abstract superclass for all bundle providers. It exposes diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 5208fe2e03f..926794f41cf 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -40,6 +40,12 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.OPTIONS; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.hapi.rest.server.ServerConformanceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -59,12 +65,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import javax.ws.rs.GET; -import javax.ws.rs.OPTIONS; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** * This is the conformance provider for the jax rs servers. It requires all providers to be registered during startup because the conformance profile is generated during the postconstruct phase. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java index 86626914d35..133791e8c63 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java @@ -30,14 +30,14 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.PageProvider; import ca.uhn.fhir.rest.server.method.PageMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; -import javax.interceptor.Interceptors; -import javax.ws.rs.GET; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** * Base class for a provider to provide the [baseUrl]?_getpages=foo request, which is a request to the diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java index 6d3e3b17cd2..b297ee90daa 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java @@ -29,16 +29,16 @@ import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.*; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.Map.Entry; import java.util.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; /** * This is the abstract superclass for all jaxrs providers. It contains some defaults implementing @@ -103,7 +103,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults { /** * This method returns the server base, independent of the request or resource. * - * @see javax.ws.rs.core.UriInfo#getBaseUri() + * @see jakarta.ws.rs.core.UriInfo#getBaseUri() * @return the ascii string for the server base */ public String getBaseForServer() { diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java index ebb634294af..5feb325154e 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java @@ -30,14 +30,21 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; import java.net.URL; -import javax.interceptor.Interceptors; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; /** * This server is the abstract superclass for all resource providers. It exposes diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java index 19b046cf2d9..f58be619bec 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java @@ -24,12 +24,12 @@ import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.InvocationContext; +import jakarta.servlet.ServletException; +import jakarta.ws.rs.core.Response; import java.io.IOException; -import javax.interceptor.AroundInvoke; -import javax.interceptor.InvocationContext; -import javax.servlet.ServletException; -import javax.ws.rs.core.Response; /** * An interceptor that catches the jax-rs exceptions diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java index 8b14284e530..14100369b86 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.jaxrs.server.interceptor; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; - -import javax.ejb.ApplicationException; +import jakarta.ejb.ApplicationException; /** * A JEE wrapper exception that will not force a rollback. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java index 3fd75c52e6f..52a9177ffd5 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.ResourceParameter; import ca.uhn.fhir.util.UrlUtil; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -42,8 +44,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; /** * The JaxRsRequest is a jax-rs specific implementation of the RequestDetails. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java index 5d08b17f87e..94015f91c42 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.jaxrs.server.util; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.BaseRestfulResponse; import ca.uhn.fhir.util.IoUtil; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.ResponseBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -33,8 +35,6 @@ import java.io.Writer; import java.util.List; import java.util.Map.Entry; import javax.annotation.Nonnull; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java index 4392426bc20..725c8268525 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java @@ -27,38 +27,26 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.io.Charsets; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.ArrayList; import java.util.Date; -import java.util.Enumeration; import java.util.List; -import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -73,23 +61,15 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericJaxRsClientDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericJaxRsClientDstu2Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + + private static final FhirContext ourCtx = FhirContext.forDstu2(); + + private static final RequestCaptureServlet CAPTURE_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(CAPTURE_SERVLET) + .keepAliveBetweenTests(); @BeforeEach public void before() { @@ -97,7 +77,7 @@ public class GenericJaxRsClientDstu2Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + CAPTURE_SERVLET.reset(); } private String getPatientFeedWithOneResult() { @@ -129,28 +109,28 @@ public class GenericJaxRsClientDstu2Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @@ -164,24 +144,24 @@ public class GenericJaxRsClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -194,23 +174,23 @@ public class GenericJaxRsClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", CAPTURE_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", CAPTURE_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -223,16 +203,16 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Conformance resp = client.capabilities().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); } @@ -245,7 +225,7 @@ public class GenericJaxRsClientDstu2Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { assertEquals(Msg.code(1355) + "JaxRsRestfulClientFactory does not have FhirContext defined. This must be set via JaxRsRestfulClientFactory#setFhirContext(FhirContext)", e.getMessage()); @@ -254,7 +234,7 @@ public class GenericJaxRsClientDstu2Test { @Test public void testCreate() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -262,23 +242,23 @@ public class GenericJaxRsClientDstu2Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - String body = ourRequestBodyString; + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + String body = CAPTURE_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); } @@ -287,39 +267,39 @@ public class GenericJaxRsClientDstu2Test { public void testCreateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); } @@ -328,22 +308,22 @@ public class GenericJaxRsClientDstu2Test { public void testCreatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -355,11 +335,11 @@ public class GenericJaxRsClientDstu2Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -372,38 +352,38 @@ public class GenericJaxRsClientDstu2Test { @Test public void testDeleteConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); } @Test public void testDelete() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); } @@ -412,10 +392,10 @@ public class GenericJaxRsClientDstu2Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -427,7 +407,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -439,7 +419,7 @@ public class GenericJaxRsClientDstu2Test { .count(null) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -450,7 +430,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt()) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -460,7 +440,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -470,7 +450,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -482,7 +462,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z")) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); @@ -493,7 +473,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) .execute(); - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -508,10 +488,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client @@ -522,10 +502,10 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); } @@ -541,10 +521,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client @@ -553,9 +533,9 @@ public class GenericJaxRsClientDstu2Test { .fromServer() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -564,9 +544,9 @@ public class GenericJaxRsClientDstu2Test { .fromType("Patient") .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -575,9 +555,9 @@ public class GenericJaxRsClientDstu2Test { .fromResource(new IdDt("Patient/123")) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); } @@ -597,10 +577,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -611,9 +591,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -624,9 +604,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -637,9 +617,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -651,7 +631,7 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); } @@ -664,10 +644,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -678,9 +658,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -691,9 +671,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -704,9 +684,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -718,18 +698,18 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -765,10 +745,10 @@ public class GenericJaxRsClientDstu2Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -777,11 +757,11 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp.getParameter().get(0).getResource().getClass()); @@ -796,10 +776,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -811,12 +791,12 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); - assertEquals("", (ourRequestBodyString)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", (CAPTURE_SERVLET.ourRequestBodyString)); /* @@ -833,13 +813,13 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals("", - (ourRequestBodyString)); + (CAPTURE_SERVLET.ourRequestBodyString)); /* @@ -856,21 +836,21 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (CAPTURE_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -928,10 +908,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client @@ -944,7 +924,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", CAPTURE_SERVLET.ourRequestUri); client @@ -958,9 +938,9 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", CAPTURE_SERVLET.ourRequestUri); + ourLog.info(CAPTURE_SERVLET.ourRequestBodyString); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); } @@ -979,10 +959,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -991,12 +971,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1005,12 +985,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1021,17 +1001,17 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @@ -1048,10 +1028,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -1060,12 +1040,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1074,12 +1054,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1088,12 +1068,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -1104,22 +1084,22 @@ public class GenericJaxRsClientDstu2Test { .withNoParameters(Parameters.class) .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @Test public void testPageNext() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client @@ -1129,14 +1109,14 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", CAPTURE_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); try { @@ -1150,14 +1130,14 @@ public class GenericJaxRsClientDstu2Test { public void testPagePrev() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client @@ -1167,7 +1147,7 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", CAPTURE_SERVLET.ourRequestUri); /* @@ -1175,7 +1155,7 @@ public class GenericJaxRsClientDstu2Test { */ sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); resp = client @@ -1185,7 +1165,7 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", CAPTURE_SERVLET.ourRequestUri); } @@ -1197,16 +1177,16 @@ public class GenericJaxRsClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1217,15 +1197,15 @@ public class GenericJaxRsClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = client.read().resource(Patient.class).withUrl(new IdDt("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = client.read().resource(Patient.class).withUrl(new IdDt(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", CAPTURE_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1248,10 +1228,10 @@ public class GenericJaxRsClientDstu2Test { ""; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -1269,10 +1249,10 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); IBaseResource response = client.read() .resource("Patient") @@ -1280,7 +1260,7 @@ public class GenericJaxRsClientDstu2Test { .elementsSubset("name", "identifier") .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1290,11 +1270,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); try { @@ -1314,11 +1294,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response = client.read() .resource(Patient.class) @@ -1326,7 +1306,7 @@ public class GenericJaxRsClientDstu2Test { .summaryMode(SummaryEnum.TEXT) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); @@ -1337,11 +1317,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1349,7 +1329,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1360,19 +1340,19 @@ public class GenericJaxRsClientDstu2Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1382,7 +1362,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1392,7 +1372,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1401,7 +1381,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1410,7 +1390,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1429,11 +1409,11 @@ public class GenericJaxRsClientDstu2Test { String msg = IOUtils.toString(GenericJaxRsClientDstu2Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() @@ -1458,11 +1438,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1473,7 +1453,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1483,11 +1463,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1499,18 +1479,18 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", CAPTURE_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", CAPTURE_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", CAPTURE_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, CAPTURE_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(CAPTURE_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test @@ -1518,11 +1498,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1535,18 +1515,18 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", CAPTURE_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", CAPTURE_SERVLET.ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, CAPTURE_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test @@ -1554,11 +1534,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1569,7 +1549,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1579,11 +1559,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1596,7 +1576,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1608,11 +1588,11 @@ public class GenericJaxRsClientDstu2Test { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1623,7 +1603,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", CAPTURE_SERVLET.ourRequestUri); } @@ -1632,11 +1612,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1647,7 +1627,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1661,12 +1641,12 @@ public class GenericJaxRsClientDstu2Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); - List input = new ArrayList(); + List input = new ArrayList<>(); Patient p1 = new Patient(); // No ID p1.addName().addFamily("PATIENT1"); @@ -1684,10 +1664,10 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", CAPTURE_SERVLET.ourRequestUri); assertEquals(2, response.size()); - String requestString = ourRequestBodyString; + String requestString = CAPTURE_SERVLET.ourRequestBodyString; ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString); assertEquals(2, requestBundle.getEntry().size()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); @@ -1720,10 +1700,10 @@ public class GenericJaxRsClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); String response = client.transaction() @@ -1731,9 +1711,9 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", CAPTURE_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); - assertEquals("application/json+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals("application/json+fhir;charset=UTF-8", CAPTURE_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); response = client.transaction() @@ -1742,8 +1722,8 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); - assertEquals("application/xml+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals(ourServer.getBaseUrl() + "/fhir/", CAPTURE_SERVLET.ourRequestUri); + assertEquals("application/xml+fhir;charset=UTF-8", CAPTURE_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); } @@ -1756,10 +1736,10 @@ public class GenericJaxRsClientDstu2Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle input = new ca.uhn.fhir.model.dstu2.resource.Bundle(); @@ -1779,7 +1759,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", CAPTURE_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1790,52 +1770,52 @@ public class GenericJaxRsClientDstu2Test { public void testUpdateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", CAPTURE_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", CAPTURE_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", CAPTURE_SERVLET.ourRequestUri); } @@ -1844,9 +1824,9 @@ public class GenericJaxRsClientDstu2Test { public void testUpdateNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); @@ -1854,19 +1834,19 @@ public class GenericJaxRsClientDstu2Test { p.addName().addFamily("FOOFAMILY"); client.update(new IdDt("Patient/123"), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); } @@ -1874,9 +1854,9 @@ public class GenericJaxRsClientDstu2Test { public void testUpdatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -1884,13 +1864,13 @@ public class GenericJaxRsClientDstu2Test { p.addName().addFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -1902,11 +1882,11 @@ public class GenericJaxRsClientDstu2Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -1925,10 +1905,10 @@ public class GenericJaxRsClientDstu2Test { final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -1937,33 +1917,33 @@ public class GenericJaxRsClientDstu2Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); @@ -1975,10 +1955,10 @@ public class GenericJaxRsClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -1991,9 +1971,9 @@ public class GenericJaxRsClientDstu2Test { response = client.validate(p); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); @@ -2003,77 +1983,4 @@ public class GenericJaxRsClientDstu2Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java index 40d7a62d37b..6826becadcd 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java @@ -19,6 +19,8 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -55,8 +57,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -77,23 +81,14 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericJaxRsClientDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericJaxRsClientDstu3Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + private static final FhirContext ourCtx = FhirContext.forDstu3(); + + private static final RequestCaptureServlet MY_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(MY_SERVLET) + .keepAliveBetweenTests(); @BeforeEach public void before() { @@ -101,7 +96,7 @@ public class GenericJaxRsClientDstu3Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + MY_SERVLET.reset(); HapiSystemProperties.enableHapiClientKeepResponses(); } @@ -134,28 +129,28 @@ public class GenericJaxRsClientDstu3Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(CapabilityStatement.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); client.fetchConformance().ofType(CapabilityStatement.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(CapabilityStatement.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @@ -169,24 +164,24 @@ public class GenericJaxRsClientDstu3Test { final Patient patient = new Patient(); patient.addName().setFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdType("123").getValue()); assertEquals("FAMILY", resp.getName().get(0).getFamily()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -199,23 +194,23 @@ public class GenericJaxRsClientDstu3Test { final Patient patient = new Patient(); patient.addName().setFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdType("123").getValue()); assertEquals("FAMILY", resp.getName().get(0).getFamily()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -227,19 +222,19 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off CapabilityStatement resp = client.fetchConformance().ofType(CapabilityStatement.class).execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @@ -252,7 +247,7 @@ public class GenericJaxRsClientDstu3Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { assertEquals(Msg.code(1355) + "JaxRsRestfulClientFactory does not have FhirContext defined. This must be set via JaxRsRestfulClientFactory#setFhirContext(FhirContext)", e.getMessage()); @@ -261,7 +256,7 @@ public class GenericJaxRsClientDstu3Test { @Test public void testCreate() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -269,23 +264,23 @@ public class GenericJaxRsClientDstu3Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - String body = ourRequestBodyString; + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + String body = MY_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @@ -294,48 +289,48 @@ public class GenericJaxRsClientDstu3Test { public void testCreateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @Test public void testCreate2() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -343,11 +338,11 @@ public class GenericJaxRsClientDstu3Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @@ -355,22 +350,22 @@ public class GenericJaxRsClientDstu3Test { public void testCreatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -382,11 +377,11 @@ public class GenericJaxRsClientDstu3Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdType("1")); @@ -399,24 +394,24 @@ public class GenericJaxRsClientDstu3Test { @Test public void testDeleteConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdType("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); } @@ -424,14 +419,14 @@ public class GenericJaxRsClientDstu3Test { @SuppressWarnings("deprecation") @Test public void testDeleteNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdType("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); } @@ -440,10 +435,10 @@ public class GenericJaxRsClientDstu3Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle response; @@ -454,7 +449,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -467,7 +462,7 @@ public class GenericJaxRsClientDstu3Test { .count(null) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -479,7 +474,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType()) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -490,7 +485,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -501,7 +496,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -514,7 +509,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType("2001-01-02T11:22:33Z")) .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); @@ -526,7 +521,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType("2001-01-02T11:22:33Z").getValue()) .execute(); //@formatter:on - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -541,10 +536,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -556,10 +551,10 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); } @@ -575,10 +570,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -588,9 +583,9 @@ public class GenericJaxRsClientDstu3Test { .fromServer() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -600,9 +595,9 @@ public class GenericJaxRsClientDstu3Test { .fromType("Patient") .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -612,9 +607,9 @@ public class GenericJaxRsClientDstu3Test { .fromResource(new IdType("Patient/123")) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @@ -634,10 +629,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -649,9 +644,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -663,9 +658,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -677,9 +672,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -691,7 +686,7 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); } @@ -704,10 +699,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -719,9 +714,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -733,9 +728,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -747,9 +742,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -761,18 +756,18 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -808,10 +803,10 @@ public class GenericJaxRsClientDstu3Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -823,11 +818,11 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(org.hl7.fhir.dstu3.model.Bundle.class, resp.getParameter().get(0).getResource().getClass()); @@ -842,10 +837,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -858,12 +853,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); - assertEquals("", (ourRequestBodyString)); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", (MY_SERVLET.ourRequestBodyString)); /* @@ -880,13 +875,13 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals("", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* @@ -903,21 +898,21 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -974,10 +969,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -991,7 +986,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:off - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", MY_SERVLET.ourRequestUri); //@formatter:off @@ -1005,9 +1000,9 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:off - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", MY_SERVLET.ourRequestUri); + ourLog.info(MY_SERVLET.ourRequestBodyString); + assertEquals("", MY_SERVLET.ourRequestBodyString); } @@ -1026,10 +1021,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -1041,12 +1036,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1058,12 +1053,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1075,17 +1070,17 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @@ -1102,10 +1097,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -1115,12 +1110,12 @@ public class GenericJaxRsClientDstu3Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1130,12 +1125,12 @@ public class GenericJaxRsClientDstu3Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1147,12 +1142,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -1163,22 +1158,22 @@ public class GenericJaxRsClientDstu3Test { .withNoParameters(Parameters.class) .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testPageNext() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle resp = client @@ -1188,14 +1183,14 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", MY_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); try { @@ -1209,14 +1204,14 @@ public class GenericJaxRsClientDstu3Test { public void testPagePrev() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle resp = client @@ -1226,7 +1221,7 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); /* @@ -1234,7 +1229,7 @@ public class GenericJaxRsClientDstu3Test { */ sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); //@formatter:off resp = client @@ -1244,7 +1239,7 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); } @@ -1256,16 +1251,16 @@ public class GenericJaxRsClientDstu3Test { patient.addName().setFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily()); } @@ -1276,15 +1271,15 @@ public class GenericJaxRsClientDstu3Test { patient.addName().setFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = client.read().resource(Patient.class).withUrl(new IdType("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = client.read().resource(Patient.class).withUrl(new IdType(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily()); } @@ -1308,10 +1303,10 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle response; @@ -1331,10 +1326,10 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off IBaseResource response = client.read() @@ -1344,7 +1339,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1354,11 +1349,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off try { @@ -1379,11 +1374,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Patient response = client.read() @@ -1393,7 +1388,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); @@ -1404,11 +1399,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1418,7 +1413,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1429,20 +1424,20 @@ public class GenericJaxRsClientDstu3Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1453,7 +1448,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1464,7 +1459,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1474,7 +1469,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1484,7 +1479,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1503,11 +1498,11 @@ public class GenericJaxRsClientDstu3Test { String msg = IOUtils.toString(GenericJaxRsClientDstu3Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle response = client.search() @@ -1532,11 +1527,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1547,7 +1542,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1557,11 +1552,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1573,18 +1568,18 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", MY_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(MY_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test @@ -1592,11 +1587,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1609,18 +1604,18 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(MY_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType); + assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test @@ -1628,11 +1623,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1643,7 +1638,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1653,11 +1648,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1670,7 +1665,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1682,11 +1677,11 @@ public class GenericJaxRsClientDstu3Test { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1697,7 +1692,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", MY_SERVLET.ourRequestUri); } @@ -1706,11 +1701,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1721,7 +1716,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1735,10 +1730,10 @@ public class GenericJaxRsClientDstu3Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); List input = new ArrayList(); @@ -1758,10 +1753,10 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.size()); - String requestString = ourRequestBodyString; + String requestString = MY_SERVLET.ourRequestBodyString; org.hl7.fhir.dstu3.model.Bundle requestBundle = ourCtx.newJsonParser().parseResource(org.hl7.fhir.dstu3.model.Bundle.class, requestString); assertEquals(2, requestBundle.getEntry().size()); assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod()); @@ -1810,10 +1805,10 @@ public class GenericJaxRsClientDstu3Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off String response = client.transaction() @@ -1821,9 +1816,9 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); - assertEquals("application/fhir+json;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals("application/fhir+json;charset=UTF-8", MY_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); //@formatter:off response = client.transaction() @@ -1832,8 +1827,8 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); - assertEquals("application/fhir+xml;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); + assertEquals("application/fhir+xml;charset=UTF-8", MY_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); } @@ -1846,10 +1841,10 @@ public class GenericJaxRsClientDstu3Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle input = new org.hl7.fhir.dstu3.model.Bundle(); @@ -1869,7 +1864,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1880,52 +1875,52 @@ public class GenericJaxRsClientDstu3Test { public void testUpdateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); } @@ -1934,9 +1929,9 @@ public class GenericJaxRsClientDstu3Test { public void testUpdateNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); @@ -1944,29 +1939,27 @@ public class GenericJaxRsClientDstu3Test { p.addName().setFamily("FOOFAMILY"); client.update(new IdType("Patient/123").getValue(), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); } @Test public void testUpdatePrefer() { + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -1974,13 +1967,13 @@ public class GenericJaxRsClientDstu3Test { p.addName().setFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -1992,11 +1985,11 @@ public class GenericJaxRsClientDstu3Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdType("1")); @@ -2015,10 +2008,10 @@ public class GenericJaxRsClientDstu3Test { final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -2027,33 +2020,33 @@ public class GenericJaxRsClientDstu3Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); @@ -2065,10 +2058,10 @@ public class GenericJaxRsClientDstu3Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -2081,9 +2074,9 @@ public class GenericJaxRsClientDstu3Test { response = client.validate(p); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", ((OperationOutcome) response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); @@ -2093,77 +2086,4 @@ public class GenericJaxRsClientDstu3Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java index 7a6a8df614a..6e701a28a24 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java @@ -14,8 +14,8 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; -import javax.ws.rs.client.Client; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Response; import java.util.ArrayList; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java index 99bbe79ec96..afb4ea4b538 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.jaxrs.client; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientResponseContext; +import jakarta.ws.rs.client.ClientResponseFilter; +import jakarta.ws.rs.ext.Provider; import java.io.IOException; /** diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java index 5354a5664b0..c7296101b31 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java index eaf56d26e28..ec39710d611 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java @@ -9,10 +9,10 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java index 6c8ebb7190b..4b026a3f51f 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java index 78725548b92..5492f0ec91d 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java index f64dbe68f4d..e1f4825f0fe 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java @@ -8,9 +8,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java index 6abf8f9b82d..d3ff832da32 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java @@ -14,10 +14,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java index 620a8b3dc1c..2b136dd03c0 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java @@ -20,8 +20,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java index 67b5fff1d67..a0c1c6ecd82 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java @@ -32,8 +32,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java index 044d00b10b7..c0d1de719a2 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java @@ -1,26 +1,35 @@ package ca.uhn.fhir.jaxrs.server.interceptor; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.*; - -import java.net.URI; -import java.util.HashMap; - -import javax.interceptor.InvocationContext; -import javax.servlet.ServletException; -import javax.ws.rs.core.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; +import jakarta.interceptor.InvocationContext; +import jakarta.servlet.ServletException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +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.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; public class JaxRsExceptionInterceptorTest { @@ -89,7 +98,7 @@ public class JaxRsExceptionInterceptorTest { when(context.proceed()).thenThrow(new ServletException()); JaxRsResponseException thrownException = new JaxRsResponseException(new NotImplementedOperationException("not implemented")); - doThrow(new javax.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException); + doThrow(new jakarta.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException); Response result = interceptor.convertExceptionIntoResponse(request, thrownException); assertEquals(InternalErrorException.STATUS_CODE, result.getStatus()); } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java index bfa5d6b8b8f..58a7a911f7a 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java @@ -1,14 +1,12 @@ package ca.uhn.fhir.jaxrs.server.interceptor; +import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import jakarta.ejb.ApplicationException; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import javax.ejb.ApplicationException; - -import org.junit.jupiter.api.Test; - -import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; - public class JaxRsResponseExceptionTest { @Test diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java index 7d9231b0930..30c3ddba7c8 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java @@ -3,11 +3,11 @@ package ca.uhn.fhir.jaxrs.server.test; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; -import javax.ejb.Stateless; import java.util.concurrent.ConcurrentHashMap; /** diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java index 335272b506d..d9b1b675eae 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java @@ -1,17 +1,15 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ejb.Stateless; - -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import java.util.concurrent.ConcurrentHashMap; /** * A conformance provider exposes the mock patient and this provider diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java index f489a7290f4..cff9cf63005 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java @@ -1,13 +1,12 @@ package ca.uhn.fhir.jaxrs.server.test; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/") @Stateless diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java index aecb104681f..25a140ec821 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.jaxrs.server.test; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java index 0f3d26d6fec..c765cecd6ea 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java @@ -1,34 +1,48 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.server.SimpleBundleProvider; -import org.hl7.fhir.instance.model.api.IIdType; -import org.mockito.Mockito; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.resource.*; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; +import ca.uhn.fhir.model.dstu2.resource.Parameters; +import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.instance.model.api.IIdType; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java index 99687058ced..963fe0b1779 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java @@ -2,11 +2,11 @@ package ca.uhn.fhir.jaxrs.server.test; import java.util.List; -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu2.model.*; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java index 08aab47036e..973fc38d8da 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java @@ -1,27 +1,46 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.hl7.fhir.dstu2016may.model.*; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.mockito.Mockito; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.dstu2016may.model.IdType; +import org.hl7.fhir.dstu2016may.model.OperationOutcome; +import org.hl7.fhir.dstu2016may.model.Parameters; +import org.hl7.fhir.dstu2016may.model.Patient; +import org.hl7.fhir.dstu2016may.model.StringType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java index 398734ea1e3..03a1d81de9b 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java @@ -2,11 +2,11 @@ package ca.uhn.fhir.jaxrs.server.test; import java.util.List; -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java index 0b34916b778..ccaaa2664df 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java @@ -1,27 +1,46 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.mockito.Mockito; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java index a9d22cac006..3fba820df81 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java index f4badf06e66..5502f8acdc9 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java @@ -3,15 +3,14 @@ package ca.uhn.fhir.jaxrs.server.util; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import org.apache.commons.lang3.StringUtils; import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java index 9963c14f8b3..17842eeb0e1 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java @@ -9,7 +9,7 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.Set; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseBinary; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java index 3d296572414..c743127d1e5 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java @@ -10,7 +10,7 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.Set; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IBaseBinary; diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 076d8328a69..b6830dcdfaf 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,14 +35,6 @@
    - - org.hibernate - hibernate-entitymanager - - - org.hibernate - hibernate-java8 - org.hibernate.validator hibernate-validator @@ -59,7 +51,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.apache.logging.log4j diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java index e00f858b49f..6dd0a286b55 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.config; import com.google.common.base.Strings; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.query.criteria.LiteralHandlingMode; +import org.hibernate.query.criteria.ValueHandlingMode; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.orm.hibernate5.SpringBeanContainer; @@ -51,13 +51,14 @@ public class HapiFhirLocalContainerEntityManagerFactoryBean extends LocalContain Map retVal = super.getJpaPropertyMap(); // SOMEDAY these defaults can be set in the constructor. setJpaProperties does a merge. - if (!retVal.containsKey(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE)) { - retVal.put(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE, LiteralHandlingMode.BIND); + if (!retVal.containsKey(AvailableSettings.CRITERIA_VALUE_HANDLING_MODE)) { + retVal.put(AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, ValueHandlingMode.BIND); } if (!retVal.containsKey(AvailableSettings.CONNECTION_HANDLING)) { retVal.put( - AvailableSettings.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD); + AvailableSettings.CONNECTION_HANDLING, + PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION); } /* diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java index 58672a138a2..2bd359b21dd 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.quartz.JobKey; import org.quartz.SchedulerException; import org.slf4j.Logger; @@ -41,7 +42,6 @@ import org.springframework.core.env.Environment; import java.util.Collection; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.PostConstruct; /** * This class provides task scheduling for the entire module using the Quartz library. diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java index b83b99a13d9..7216e9fef42 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java @@ -19,33 +19,32 @@ */ package ca.uhn.fhir.jpa.util; -import org.hibernate.dialect.DerbyTenSevenDialect; -import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; -import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; +import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.SQLException; +import java.util.function.Function; -public class DerbyTenSevenHapiFhirDialect extends DerbyTenSevenDialect { +import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; + +public class DerbyTenSevenHapiFhirDialect extends DerbyDialect { private static final Logger ourLog = LoggerFactory.getLogger(DerbyTenSevenHapiFhirDialect.class); @Override - public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { - return new TemplatedViolatedConstraintNameExtracter() { - @Override - protected String doExtractConstraintName(SQLException theSqlException) throws NumberFormatException { - switch (theSqlException.getSQLState()) { - case "23505": - return this.extractUsingTemplate( - "unique or primary key constraint or unique index identified by '", - "'", - theSqlException.getMessage()); - default: - return null; - } + public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { + Function extractor = e -> { + switch (e.getSQLState()) { + case "23505": + return extractUsingTemplate( + "unique or primary key constraint or unique index identified by '", "'", e.getMessage()); + default: + return null; } }; + return new TemplatedViolatedConstraintNameExtractor(extractor); } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java similarity index 87% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java rename to hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java index df4c3a7ba52..a7bfc43a1bc 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR JPA Model + * hapi-fhir-jpa * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -17,16 +17,18 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.model.dialect; +package ca.uhn.fhir.jpa.util; + +import org.hibernate.service.Service; import javax.annotation.Nullable; /** * This is an internal API and may change or disappear without notice * - * Implementations of this interface can modify the automatically generated sequence values created by hibernate seuqnece generator + * Implementations of this interface can modify the automatically generated sequence values created by hibernate sequence generator */ -public interface ISequenceValueMassager { +public interface ISequenceValueMassager extends Service { Long massage(String theGeneratorName, Long theId); diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index da09b2a7d82..915810128cf 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -208,7 +208,7 @@ org.springdoc - springdoc-openapi-ui + springdoc-openapi-starter-webmvc-ui @@ -217,7 +217,7 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 @@ -243,23 +243,12 @@ xml-patch - - - - - javax.interceptor - javax.interceptor-api - provided - - - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -308,11 +297,6 @@ org.springframework spring-websocket - - javax.el - javax.el-api - provided - org.glassfish jakarta.el @@ -325,12 +309,12 @@ - org.elasticsearch.client - elasticsearch-rest-high-level-client + co.elastic.clients + elasticsearch-java - org.apache.logging.log4j - log4j-api + jakarta.json + jakarta.json-api @@ -347,7 +331,7 @@ hibernate-search-backend-elasticsearch-aws - org.hibernate + org.hibernate.orm hibernate-envers @@ -435,98 +419,7 @@ - - de.jpdigital - hibernate56-ddl-maven-plugin - - - derby_10_7 - mysql57 - mariadb - oracle12c - sqlserver2012 - - - ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect - ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - org.hibernate.dialect.CockroachDB201Dialect - - ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database - - ca.uhn.fhir.jpa.entity - ca.uhn.fhir.jpa.model.entity - - - - - process-classes - - gen-ddl - - - - - - - org.glassfish.jaxb - jaxb-runtime - ${jaxb_runtime_version} - - - ca.uhn.hapi.fhir - hapi-fhir-jpaserver-model - ${project.version} - - - org.hibernate - hibernate-core - ${hibernate_version} - - - org.hibernate - hibernate-envers - - ${hibernate_version} - - - javax.xml.bind - jaxb-api - ${jaxb_api_version} - - - com.sun.xml.bind - jaxb-impl - ${jaxb_api_version} - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - process-classes - - run - - - - - - - - - - - - - - - - + ca.uhn.hapi.fhir hapi-tinder-plugin @@ -637,6 +530,68 @@ + + + ca.uhn.hapi.fhir + hapi-tinder-plugin + ${project.version} + + + + generate-ddl + + + + + + ca.uhn.fhir.jpa.entity + ca.uhn.fhir.jpa.model.entity + + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirDerbyDialect + derby.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect + mysql.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect + mariadb.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect + oracle.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirSQLServerDialect + sqlserver.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirCockroachDialect + cockroachdb.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + h2.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect + classpath:ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql + postgres.sql + + + ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database + + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-model + ${project.version} + + + org.codehaus.mojo build-helper-maven-plugin diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java index c4803afcf86..6105c06bf17 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java @@ -25,13 +25,12 @@ import ca.uhn.fhir.jpa.bulk.export.job.BulkExportJobConfig; import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import jakarta.persistence.EntityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; -import javax.persistence.EntityManager; - @Configuration @Import({BulkExportJobConfig.class}) public class JpaBatch2Config extends BaseBatch2Config { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java index 12bf7d12cec..d44374083cc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java @@ -42,6 +42,9 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Query; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -66,9 +69,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; -import javax.persistence.Query; import static ca.uhn.fhir.batch2.coordinator.WorkChunkProcessor.MAX_CHUNK_ERROR_COUNT; import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH; @@ -296,7 +296,6 @@ public class JpaJobPersistenceImpl implements IJobPersistence { } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) public WorkChunkStatusEnum onWorkChunkError(WorkChunkErrorEvent theParameters) { String chunkId = theParameters.getChunkId(); String errorMessage = truncateErrorMessage(theParameters.getErrorMsg()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java index 58936143305..f4d906f52c6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java @@ -28,6 +28,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.google.common.hash.HashingInputStream; import com.google.common.io.ByteStreams; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CountingInputStream; import org.hibernate.LobHelper; @@ -45,9 +48,6 @@ import java.sql.SQLException; import java.util.Date; import java.util.Optional; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Transactional public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java index 5d352926733..74e01095381 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.JsonUtil; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -55,7 +56,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java index 8245fdaf066..ab3c79551ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java @@ -58,6 +58,7 @@ import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.SearchParameterUtil; +import jakarta.persistence.EntityManager; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -79,7 +80,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; import static ca.uhn.fhir.rest.api.Constants.PARAM_HAS; import static ca.uhn.fhir.rest.api.Constants.PARAM_ID; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java index 301d7739466..9d9b5e9248e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.ValidateUtil; import com.apicatalog.jsonld.StringUtils; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; import org.slf4j.Logger; @@ -59,7 +60,6 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.Semaphore; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.batch2.jobs.importpull.BulkImportPullConfig.BULK_IMPORT_JOB_NAME; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java index 16cda3e728c..3c3f228adff 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java @@ -34,11 +34,10 @@ import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSqlBuilder; import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSvcImpl; import ca.uhn.fhir.jpa.reindex.Batch2DaoSvcImpl; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import jakarta.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import javax.persistence.EntityManager; - public class Batch2SupportConfig { @Bean diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java index 18fd08f7a28..be5702b2e89 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java @@ -20,13 +20,12 @@ package ca.uhn.fhir.jpa.config; * #L% */ +import jakarta.persistence.EntityManagerFactory; import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReaderFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import javax.persistence.EntityManagerFactory; - @Configuration public class EnversAuditConfig { private final EntityManagerFactory myEntityManagerFactory; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java index 463e3a471cb..3043fe69b81 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.system.HapiSystemProperties; +import jakarta.persistence.PersistenceException; import org.hibernate.HibernateException; import org.hibernate.PessimisticLockException; import org.hibernate.exception.ConstraintViolationException; @@ -36,7 +37,6 @@ import org.springframework.dao.DataAccessException; import org.springframework.orm.jpa.vendor.HibernateJpaDialect; import javax.annotation.Nonnull; -import javax.persistence.PersistenceException; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 50ec76efa26..9d752b6694c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -51,7 +51,6 @@ import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser; import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.JpaStorageResourceParser; import ca.uhn.fhir.jpa.dao.MatchResourceUrlService; -import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc; import ca.uhn.fhir.jpa.dao.SearchBuilderFactory; import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.data.IResourceModifiedDao; @@ -223,7 +222,6 @@ public class JpaConfig { public static final String PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH = "PersistedJpaBundleProvider_BySearch"; public static final String PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER = "PersistedJpaSearchFirstPageBundleProvider"; - public static final String SEARCH_BUILDER = "SearchBuilder"; public static final String HISTORY_BUILDER = "HistoryBuilder"; private static final String HAPI_DEFAULT_SCHEDULER_GROUP = "HAPI"; @@ -564,7 +562,8 @@ public class JpaConfig { @Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH) @Scope("prototype") - public PersistedJpaBundleProvider newPersistedJpaBundleProvider(RequestDetails theRequest, Search theSearch) { + public PersistedJpaBundleProvider newPersistedJpaBundleProviderBySearch( + RequestDetails theRequest, Search theSearch) { return new PersistedJpaBundleProvider(theRequest, theSearch); } @@ -694,7 +693,7 @@ public class JpaConfig { @Bean(name = HISTORY_BUILDER) @Scope("prototype") - public HistoryBuilder newPersistedJpaSearchFirstPageBundleProvider( + public HistoryBuilder newHistoryBuilder( @Nullable String theResourceType, @Nullable Long theResourceId, @Nullable Date theRangeStartInclusive, @@ -832,11 +831,6 @@ public class JpaConfig { return new TermReindexingSvcImpl(); } - @Bean - public ObservationLastNIndexPersistSvc baseObservationLastNIndexpersistSvc() { - return new ObservationLastNIndexPersistSvc(); - } - @Bean @Scope("prototype") public PersistenceContextProvider persistenceContextProvider() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java index f3c0a72e75f..1a28f92db97 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java @@ -20,30 +20,88 @@ package ca.uhn.fhir.jpa.config.util; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; +import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.persistence.spi.PersistenceUnitInfo; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; +import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import java.util.Map; + public final class HapiEntityManagerFactoryUtil { private HapiEntityManagerFactoryUtil() {} + /** * This method provides a partially completed entity manager * factory with HAPI FHIR customizations */ public static LocalContainerEntityManagerFactoryBean newEntityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory myConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = new HapiFhirLocalContainerEntityManagerFactoryBean(myConfigurableListableBeanFactory); - configureEntityManagerFactory(retVal, theFhirContext); + configureEntityManagerFactory(retVal, theFhirContext, theStorageSettings); return retVal; } public static void configureEntityManagerFactory( - LocalContainerEntityManagerFactoryBean theFactory, FhirContext theFhirContext) { + LocalContainerEntityManagerFactoryBean theFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { theFactory.setJpaDialect(new HapiFhirHibernateJpaDialect(theFhirContext.getLocalizer())); theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); - theFactory.setPersistenceProvider(new HibernatePersistenceProvider()); + theFactory.setPersistenceProvider(new MyHibernatePersistenceProvider(theStorageSettings)); + } + + private static class MyHibernatePersistenceProvider extends HibernatePersistenceProvider { + + private final JpaStorageSettings myStorageSettings; + + public MyHibernatePersistenceProvider(JpaStorageSettings theStorageSettings) { + myStorageSettings = theStorageSettings; + } + + /** + * @see MyEntityManagerFactoryBuilderImpl for an explanation of why we do this + */ + @Override + protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder( + PersistenceUnitInfo info, Map integration) { + return new MyEntityManagerFactoryBuilderImpl(info, integration); + } + + /** + * This class extends the default hibernate EntityManagerFactoryBuilder in order to + * register a custom service (the {@link ISequenceValueMassager}, which is used in + * {@link ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator}. + *

    + * In Hibernate 5 we didn't need to do this, since we could just register + * the service with Spring and Hibernate would ask Spring for it. This no longer + * seems to work in Hibernate 6, so we now have to manually register it. + */ + private class MyEntityManagerFactoryBuilderImpl extends EntityManagerFactoryBuilderImpl { + public MyEntityManagerFactoryBuilderImpl(PersistenceUnitInfo theInfo, Map theIntegration) { + super(new PersistenceUnitInfoDescriptor(theInfo), (Map) theIntegration); + } + + @Override + protected StandardServiceRegistryBuilder getStandardServiceRegistryBuilder(BootstrapServiceRegistry bsr) { + StandardServiceRegistryBuilder retVal = super.getStandardServiceRegistryBuilder(bsr); + ISequenceValueMassager sequenceValueMassager = + ReflectionUtil.newInstance(myStorageSettings.getSequenceValueMassagerClass()); + retVal.addService(ISequenceValueMassager.class, sequenceValueMassager); + return retVal; + } + } } } 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 13dfc7b25f0..af3d76dead1 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 @@ -107,6 +107,16 @@ import com.google.common.collect.Sets; import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -151,16 +161,6 @@ import java.util.StringTokenizer; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import javax.xml.stream.events.Characters; import javax.xml.stream.events.XMLEvent; 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 ba1a21a0eea..4d58d571ca7 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 @@ -118,6 +118,11 @@ import ca.uhn.fhir.validation.ValidationOptions; import ca.uhn.fhir.validation.ValidationResult; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Streams; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.LockModeType; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseCoding; @@ -129,7 +134,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Required; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.transaction.PlatformTransactionManager; @@ -155,11 +159,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.LockModeType; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; -import javax.servlet.http.HttpServletResponse; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -1224,7 +1223,6 @@ public abstract class BaseHapiFhirResourceDao extends B } @SuppressWarnings("unchecked") - @Required public void setResourceType(Class theTableType) { myResourceType = (Class) theTableType; } @@ -1657,8 +1655,11 @@ public abstract class BaseHapiFhirResourceDao extends B CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE); } + SystemRequestDetails request = new SystemRequestDetails(); + request.getUserData().put(JpaConstants.SKIP_REINDEX_ON_UPDATE, Boolean.TRUE); + updateEntity( - null, theResource, theEntity, theEntity.getDeleted(), true, false, transactionDetails, true, false); + request, theResource, theEntity, theEntity.getDeleted(), true, false, transactionDetails, true, false); if (theResource != null) { CURRENTLY_REINDEXING.put(theResource, null); } 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 6696a3b0e52..961992748e1 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 @@ -48,6 +48,15 @@ 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.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +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; @@ -61,15 +70,6 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; public abstract class BaseHapiFhirSystemDao extends BaseStorageDao implements IFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index b9ab01c5c6a..76e6279ddd2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -23,11 +23,11 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java index 346b1063e3d..80bd0e9a7f1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java @@ -53,6 +53,9 @@ import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import com.google.common.collect.Ordering; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension; import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep; import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory; @@ -79,9 +82,6 @@ import java.util.Spliterators; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.rest.server.BasePagingProvider.DEFAULT_MAX_PAGE_SIZE; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java index 445e9d8860b..30a181df720 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java @@ -32,6 +32,17 @@ import ca.uhn.fhir.rest.param.HistorySearchStyleEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimaps; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -43,17 +54,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Subquery; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.toPredicateArray; @@ -177,18 +177,13 @@ public class HistoryBuilder { if (!thePartitionId.isAllPartitions()) { if (thePartitionId.isDefaultPartition()) { - predicates.add(theCriteriaBuilder.isNull( - theFrom.get("myPartitionIdValue").as(Integer.class))); + predicates.add(theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue"))); } else if (thePartitionId.hasDefaultPartitionId()) { predicates.add(theCriteriaBuilder.or( - theCriteriaBuilder.isNull( - theFrom.get("myPartitionIdValue").as(Integer.class)), - theFrom.get("myPartitionIdValue") - .as(Integer.class) - .in(thePartitionId.getPartitionIdsWithoutDefault()))); + theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue")), + theFrom.get("myPartitionIdValue").in(thePartitionId.getPartitionIdsWithoutDefault()))); } else { - predicates.add( - theFrom.get("myPartitionIdValue").as(Integer.class).in(thePartitionId.getPartitionIds())); + predicates.add(theFrom.get("myPartitionIdValue").in(thePartitionId.getPartitionIds())); } } @@ -205,13 +200,12 @@ public class HistoryBuilder { if (HistorySearchStyleEnum.AT == theHistorySearchStyle && myResourceId != null) { addPredicateForAtQueryParameter(theCriteriaBuilder, theQuery, theFrom, predicates); } else { - predicates.add(theCriteriaBuilder.greaterThanOrEqualTo( - theFrom.get("myUpdated").as(Date.class), myRangeStartInclusive)); + predicates.add( + theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated"), myRangeStartInclusive)); } } if (myRangeEndInclusive != null) { - predicates.add(theCriteriaBuilder.lessThanOrEqualTo( - theFrom.get("myUpdated").as(Date.class), myRangeEndInclusive)); + predicates.add(theCriteriaBuilder.lessThanOrEqualTo(theFrom.get("myUpdated"), myRangeEndInclusive)); } if (predicates.size() > 0) { @@ -226,20 +220,19 @@ public class HistoryBuilder { List thePredicates) { Subquery pastDateSubQuery = theQuery.subquery(Date.class); Root subQueryResourceHistory = pastDateSubQuery.from(ResourceHistoryTable.class); - Expression myUpdatedMostRecent = - theCriteriaBuilder.max(subQueryResourceHistory.get("myUpdated")).as(Date.class); - Expression myUpdatedMostRecentOrDefault = + Expression myUpdatedMostRecent = theCriteriaBuilder.max(subQueryResourceHistory.get("myUpdated")); + Expression myUpdatedMostRecentOrDefault = theCriteriaBuilder.coalesce(myUpdatedMostRecent, theCriteriaBuilder.literal(myRangeStartInclusive)); pastDateSubQuery .select(myUpdatedMostRecentOrDefault) .where( theCriteriaBuilder.lessThanOrEqualTo( - subQueryResourceHistory.get("myUpdated").as(Date.class), myRangeStartInclusive), + subQueryResourceHistory.get("myUpdated"), myRangeStartInclusive), theCriteriaBuilder.equal(subQueryResourceHistory.get("myResourceId"), myResourceId)); Predicate updatedDatePredicate = - theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated").as(Date.class), pastDateSubQuery); + theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated"), pastDateSubQuery); thePredicates.add(updatedDatePredicate); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java index 78b1eed3f6d..d3c32df28f8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -58,7 +59,6 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java index 12a4f01a947..1a51e09e493 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java @@ -42,6 +42,7 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import jakarta.annotation.PostConstruct; import org.apache.commons.collections4.CollectionUtils; import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.instance.model.api.IBaseCoding; @@ -59,7 +60,6 @@ import java.util.Date; import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.util.DatatypeUtil.toStringValue; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java index 1ff023172a6..22b7da2f025 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoComposition extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java index 9c365a43b7d..6e35f2e0341 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoEncounter extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java index 6181c2dedfc..b81db1c490c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java @@ -22,9 +22,7 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.IQueryParameterType; @@ -34,22 +32,20 @@ import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Observation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.TreeMap; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.servlet.http.HttpServletResponse; public class JpaResourceDaoObservation extends BaseHapiFhirResourceDao implements IFhirResourceDaoObservation { @@ -57,9 +53,6 @@ public class JpaResourceDaoObservation extends BaseHapi @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; - @Autowired - ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc; - @Autowired private IRequestPartitionHelperSvc myRequestPartitionHelperService; @@ -98,64 +91,6 @@ public class JpaResourceDaoObservation extends BaseHapi return Observation.SP_PATIENT; } - @Override - public ResourceTable updateEntity( - RequestDetails theRequest, - IBaseResource theResource, - IBasePersistedResource theEntity, - Date theDeletedTimestampOrNull, - boolean thePerformIndexing, - boolean theUpdateVersion, - TransactionDetails theTransactionDetails, - boolean theForceUpdate, - boolean theCreateNewHistoryEntry) { - return updateObservationEntity( - theRequest, - theResource, - theEntity, - theDeletedTimestampOrNull, - thePerformIndexing, - theUpdateVersion, - theTransactionDetails, - theForceUpdate, - theCreateNewHistoryEntry); - } - - protected ResourceTable updateObservationEntity( - RequestDetails theRequest, - IBaseResource theResource, - IBasePersistedResource theEntity, - Date theDeletedTimestampOrNull, - boolean thePerformIndexing, - boolean theUpdateVersion, - TransactionDetails theTransactionDetails, - boolean theForceUpdate, - boolean theCreateNewHistoryEntry) { - ResourceTable retVal = super.updateEntity( - theRequest, - theResource, - theEntity, - theDeletedTimestampOrNull, - thePerformIndexing, - theUpdateVersion, - theTransactionDetails, - theForceUpdate, - theCreateNewHistoryEntry); - - if (getStorageSettings().isLastNEnabled()) { - if (!retVal.isUnchangedInCurrentOperation()) { - if (retVal.getDeleted() == null) { - // Update indexes here for LastN operation. - myObservationLastNIndexPersistSvc.indexObservation(theResource); - } else { - myObservationLastNIndexPersistSvc.deleteObservationIndex(theEntity); - } - } - } - - return retVal; - } - protected void updateSearchParamsForLastn( SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails) { if (!isPagingProviderDatabaseBacked(theRequestDetails)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java index 0a1a4540614..e94702fad0b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -47,7 +48,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoPatient extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java deleted file mode 100644 index fa8b6fb3274..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java +++ /dev/null @@ -1,260 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2023 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; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.IElasticsearchSvc; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; -import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef; -import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - -public class ObservationLastNIndexPersistSvc { - - @Autowired - private ISearchParamExtractor mySearchParameterExtractor; - - @Autowired(required = false) - private IElasticsearchSvc myElasticsearchSvc; - - @Autowired - private JpaStorageSettings myConfig; - - @Autowired - private FhirContext myContext; - - public void indexObservation(IBaseResource theResource) { - - if (myElasticsearchSvc == null) { - // Elasticsearch is not enabled and therefore no index needs to be updated. - return; - } - - List subjectReferenceElement = - mySearchParameterExtractor.extractValues("Observation.subject", theResource); - String subjectId = subjectReferenceElement.stream() - .map(refElement -> - mySearchParameterExtractor.extractReferenceLinkFromResource(refElement, "Observation.subject")) - .filter(Objects::nonNull) - .map(PathAndRef::getRef) - .filter(Objects::nonNull) - .map(subjectRef -> subjectRef.getReferenceElement().getValue()) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); - - Date effectiveDtm = null; - List effectiveDateElement = - mySearchParameterExtractor.extractValues("Observation.effective", theResource); - if (effectiveDateElement.size() > 0) { - effectiveDtm = mySearchParameterExtractor.extractDateFromResource( - effectiveDateElement.get(0), "Observation.effective"); - } - - List observationCodeCodeableConcepts = - mySearchParameterExtractor.extractValues("Observation.code", theResource); - - // Only index for lastn if Observation has a code - if (observationCodeCodeableConcepts.size() == 0) { - return; - } - - List observationCategoryCodeableConcepts = - mySearchParameterExtractor.extractValues("Observation.category", theResource); - - createOrUpdateIndexedObservation( - theResource, - effectiveDtm, - subjectId, - observationCodeCodeableConcepts, - observationCategoryCodeableConcepts); - } - - private void createOrUpdateIndexedObservation( - IBaseResource theResource, - Date theEffectiveDtm, - String theSubjectId, - List theObservationCodeCodeableConcepts, - List theObservationCategoryCodeableConcepts) { - String resourcePID = theResource.getIdElement().getIdPart(); - - // Determine if an index already exists for Observation: - ObservationJson indexedObservation = null; - if (resourcePID != null) { - indexedObservation = myElasticsearchSvc.getObservationDocument(resourcePID); - } - if (indexedObservation == null) { - indexedObservation = new ObservationJson(); - } - - indexedObservation.setEffectiveDtm(theEffectiveDtm); - indexedObservation.setIdentifier(resourcePID); - if (myConfig.isStoreResourceInHSearchIndex()) { - indexedObservation.setResource(encodeResource(theResource)); - } - indexedObservation.setSubject(theSubjectId); - - addCodeToObservationIndex(theObservationCodeCodeableConcepts, indexedObservation); - - addCategoriesToObservationIndex(theObservationCategoryCodeableConcepts, indexedObservation); - - myElasticsearchSvc.createOrUpdateObservationIndex(resourcePID, indexedObservation); - } - - private String encodeResource(IBaseResource theResource) { - IParser parser = myContext.newJsonParser(); - return parser.encodeResourceToString(theResource); - } - - private void addCodeToObservationIndex( - List theObservationCodeCodeableConcepts, ObservationJson theIndexedObservation) { - // Determine if a Normalized ID was created previously for Observation Code - String existingObservationCodeNormalizedId = - getCodeCodeableConceptId(theObservationCodeCodeableConcepts.get(0)); - - // Create/update normalized Observation Code index record - CodeJson codeableConceptField = - getCodeCodeableConcept(theObservationCodeCodeableConcepts.get(0), existingObservationCodeNormalizedId); - - myElasticsearchSvc.createOrUpdateObservationCodeIndex( - codeableConceptField.getCodeableConceptId(), codeableConceptField); - - theIndexedObservation.setCode(codeableConceptField); - } - - private void addCategoriesToObservationIndex( - List observationCategoryCodeableConcepts, ObservationJson indexedObservation) { - // Build CodeableConcept entities for Observation.Category - List categoryCodeableConceptEntities = new ArrayList<>(); - for (IBase categoryCodeableConcept : observationCategoryCodeableConcepts) { - // Build CodeableConcept entities for each category CodeableConcept - categoryCodeableConceptEntities.add(getCategoryCodeableConceptEntities(categoryCodeableConcept)); - } - indexedObservation.setCategories(categoryCodeableConceptEntities); - } - - private CodeJson getCategoryCodeableConceptEntities(IBase theValue) { - String text = mySearchParameterExtractor.getDisplayTextFromCodeableConcept(theValue); - CodeJson categoryCodeableConcept = new CodeJson(); - categoryCodeableConcept.setCodeableConceptText(text); - - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - for (IBase nextCoding : codings) { - addCategoryCoding(nextCoding, categoryCodeableConcept); - } - return categoryCodeableConcept; - } - - private CodeJson getCodeCodeableConcept(IBase theValue, String observationCodeNormalizedId) { - String text = mySearchParameterExtractor.getDisplayTextFromCodeableConcept(theValue); - CodeJson codeCodeableConcept = new CodeJson(); - codeCodeableConcept.setCodeableConceptText(text); - codeCodeableConcept.setCodeableConceptId(observationCodeNormalizedId); - - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - for (IBase nextCoding : codings) { - addCodeCoding(nextCoding, codeCodeableConcept); - } - - return codeCodeableConcept; - } - - private String getCodeCodeableConceptId(IBase theValue) { - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - Optional codeCodeableConceptIdOptional = Optional.empty(); - - for (IBase nextCoding : codings) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "code", null, null, null, null, null, null, null), - nextCoding); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(nextCoding); - - String codeSystemHash = String.valueOf(CodeSystemHash.hashCodeSystem(system, code)); - CodeJson codeCodeableConceptDocument = - myElasticsearchSvc.getObservationCodeDocument(codeSystemHash, text); - if (codeCodeableConceptDocument != null) { - codeCodeableConceptIdOptional = Optional.of(codeCodeableConceptDocument.getCodeableConceptId()); - break; - } - } - } - - return codeCodeableConceptIdOptional.orElse(UUID.randomUUID().toString()); - } - - private void addCategoryCoding(IBase theValue, CodeJson theCategoryCodeableConcept) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "category", null, null, null, null, null, null, null), - theValue); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(theValue); - theCategoryCodeableConcept.addCoding(system, code, text); - } - } - - private void addCodeCoding(IBase theValue, CodeJson theObservationCode) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "code", null, null, null, null, null, null, null), - theValue); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(theValue); - theObservationCode.addCoding(system, code, text); - } - } - - public void deleteObservationIndex(IBasePersistedResource theEntity) { - if (myElasticsearchSvc == null) { - // Elasticsearch is not enabled and therefore no index needs to be updated. - return; - } - - ObservationJson deletedObservationLastNEntity = - myElasticsearchSvc.getObservationDocument(theEntity.getIdDt().getIdPart()); - if (deletedObservationLastNEntity != null) { - myElasticsearchSvc.deleteObservationDocument(deletedObservationLastNEntity.getIdentifier()); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index 16d58ec0be6..437677f99c5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -44,6 +44,16 @@ import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.Validate; import org.hibernate.internal.SessionImpl; import org.hl7.fhir.instance.model.api.IBase; @@ -65,16 +75,6 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.PersistenceException; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.util.UrlUtil.determineResourceTypeInResourceUrl; import static org.apache.commons.lang3.StringUtils.countMatches; @@ -367,29 +367,25 @@ public class TransactionProcessor extends BaseTransactionProcessor { CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = cb.createTupleQuery(); Root from = cq.from(ResourceIndexedSearchParamToken.class); - cq.multiselect( - from.get("myResourcePid").as(Long.class), - from.get(theIndexColumnName).as(Long.class)); + cq.multiselect(from.get("myResourcePid"), from.get(theIndexColumnName)); Predicate masterPredicate; if (theHashesForIndexColumn.size() == 1) { masterPredicate = cb.equal( - from.get(theIndexColumnName).as(Long.class), + from.get(theIndexColumnName), theHashesForIndexColumn.iterator().next()); } else { - masterPredicate = from.get(theIndexColumnName).as(Long.class).in(theHashesForIndexColumn); + masterPredicate = from.get(theIndexColumnName).in(theHashesForIndexColumn); } if (myPartitionSettings.isPartitioningEnabled() && !myPartitionSettings.isIncludePartitionInSearchHashes()) { if (theRequestPartitionId.isDefaultPartition()) { - Predicate partitionIdCriteria = - cb.isNull(from.get("myPartitionIdValue").as(Integer.class)); + Predicate partitionIdCriteria = cb.isNull(from.get("myPartitionIdValue")); masterPredicate = cb.and(partitionIdCriteria, masterPredicate); } else if (!theRequestPartitionId.isAllPartitions()) { - Predicate partitionIdCriteria = from.get("myPartitionIdValue") - .as(Integer.class) - .in(theRequestPartitionId.getPartitionIds()); + Predicate partitionIdCriteria = + from.get("myPartitionIdValue").in(theRequestPartitionId.getPartitionIds()); masterPredicate = cb.and(partitionIdCriteria, masterPredicate); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java index bd65315bf30..b74a1b757f2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java @@ -44,30 +44,30 @@ public interface IResourceHistoryTableDao extends JpaRepository :dontWantVersion") Slice findForResourceId( Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); @Query( - "SELECT t FROM ResourceHistoryTable t LEFT OUTER JOIN FETCH t.myProvenance WHERE t.myResourceId = :resId AND t.myResourceVersion != :dontWantVersion") + "SELECT t FROM ResourceHistoryTable t LEFT OUTER JOIN FETCH t.myProvenance WHERE t.myResourceId = :resId AND t.myResourceVersion <> :dontWantVersion") Slice findForResourceIdAndReturnEntitiesAndFetchProvenance( Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion AND " + + "WHERE v.myResourceVersion <> t.myVersion AND " + "t.myId = :resId") Slice findIdsOfPreviousVersionsOfResourceId(Pageable thePage, @Param("resId") Long theResourceId); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion AND " + + "WHERE v.myResourceVersion <> t.myVersion AND " + "t.myResourceType = :restype") Slice findIdsOfPreviousVersionsOfResources(Pageable thePage, @Param("restype") String theResourceName); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion") + + "WHERE v.myResourceVersion <> t.myVersion") Slice findIdsOfPreviousVersionsOfResources(Pageable thePage); @Modifying diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java index a8d86d69e43..208e8c78d1e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java @@ -19,12 +19,12 @@ */ package ca.uhn.fhir.jpa.dao.data.custom; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; @Component /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index ced37678fdf..2865edc0c9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.instance.model.api.IBaseBundle; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { 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 34f247edce7..9c192fa58e8 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 @@ -78,6 +78,16 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.StopWatch; +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.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -85,14 +95,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; @Service public class ExpungeEverythingService implements IExpungeEverythingService { @@ -229,6 +234,7 @@ public class ExpungeEverythingService implements IExpungeEverythingService { expungeEverythingByTypeWithoutPurging(theRequest, ResourceHistoryTable.class, requestPartitionId)); counter.addAndGet( expungeEverythingByTypeWithoutPurging(theRequest, ResourceSearchUrlEntity.class, requestPartitionId)); + int counterBefore = counter.get(); counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceTable.class, requestPartitionId)); counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, PartitionEntity.class, requestPartitionId)); @@ -257,8 +263,10 @@ public class ExpungeEverythingService implements IExpungeEverythingService { myMemoryCacheService.invalidateAllCaches(); } - private int expungeEverythingByTypeWithoutPurging( - RequestDetails theRequest, Class theEntityType, RequestPartitionId theRequestPartitionId) { + private int expungeEverythingByTypeWithoutPurging( + RequestDetails theRequest, Class theEntityType, RequestPartitionId theRequestPartitionId) { + HapiTransactionService.noTransactionAllowed(); + int outcome = 0; while (true) { StopWatch sw = new StopWatch(); @@ -268,16 +276,49 @@ public class ExpungeEverythingService implements IExpungeEverythingService { .withPropagation(Propagation.REQUIRES_NEW) .withRequestPartitionId(theRequestPartitionId) .execute(() -> { - CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(theEntityType); - cq.from(theEntityType); - TypedQuery query = myEntityManager.createQuery(cq); - query.setMaxResults(1000); - List results = query.getResultList(); - for (Object result : results) { - myEntityManager.remove(result); + + /* + * This method uses a nice efficient mechanism where we figure out the PID datatype + * and load only the PIDs and delete by PID for all resource types except ResourceTable. + * We delete ResourceTable using the entitymanager so that Hibernate Search knows to + * delete the corresponding records it manages in ElasticSearch. See + * FhirResourceDaoR4SearchWithElasticSearchIT for a test that fails without the + * block below. + */ + if (ResourceTable.class.equals(theEntityType)) { + CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(theEntityType); + cq.from(theEntityType); + TypedQuery query = myEntityManager.createQuery(cq); + query.setMaxResults(800); + List results = query.getResultList(); + for (Object result : results) { + myEntityManager.remove(result); + } + return results.size(); } - return results.size(); + + Metamodel metamodel = myEntityManager.getMetamodel(); + EntityType entity = metamodel.entity(theEntityType); + Set> singularAttributes = entity.getSingularAttributes(); + String idProperty = null; + for (SingularAttribute singularAttribute : singularAttributes) { + if (singularAttribute.isId()) { + idProperty = singularAttribute.getName(); + break; + } + } + + Query nativeQuery = myEntityManager.createQuery( + "SELECT " + idProperty + " FROM " + theEntityType.getSimpleName()); + nativeQuery.setMaxResults(800); + List pids = nativeQuery.getResultList(); + + nativeQuery = myEntityManager.createQuery("DELETE FROM " + theEntityType.getSimpleName() + + " WHERE " + idProperty + " IN (:pids)"); + nativeQuery.setParameter("pids", pids); + nativeQuery.executeUpdate(); + return pids.size(); }); outcome += count; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java index 458f5a428d5..fb6dda60d86 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java @@ -24,18 +24,22 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams; import ca.uhn.fhir.jpa.util.AddRemoveCount; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Service public class DaoSearchParamSynchronizer { + private static final Logger ourLog = LoggerFactory.getLogger(DaoSearchParamSynchronizer.class); + @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; @@ -89,9 +93,13 @@ public class DaoSearchParamSynchronizer { tryToReuseIndexEntities(paramsToRemove, paramsToAdd); for (T next : paramsToRemove) { + if (!myEntityManager.contains(next)) { + // If a resource is created and deleted in the same transaction, we can end up + // in a state where we're deleting entities that don't actually exist. Hibernate + // 6 is stricter about this, so we skip here. + continue; + } myEntityManager.remove(next); - theEntity.getParamsQuantity().remove(next); - theEntity.getParamsQuantityNormalized().remove(next); } for (T next : paramsToAdd) { myEntityManager.merge(next); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index cee2c8bfd08..e23720ad33d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -42,6 +42,15 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ListMultimap; import com.google.common.collect.MultimapBuilder; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -66,15 +75,6 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder.replaceDefaultPartitionIdIfNonNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -377,10 +377,7 @@ public class IdHelperService implements IIdHelperService { * Only PG, and MSSql support INCLUDE COLUMNS. * @see AddIndexTask.generateSql */ - criteriaQuery.multiselect( - from.get("myId").as(Long.class), - from.get("myResourceType").as(String.class), - from.get("myFhirId").as(String.class)); + criteriaQuery.multiselect(from.get("myId"), from.get("myResourceType"), from.get("myFhirId")); // one create one clause per id. List predicates = new ArrayList<>(theIds.size()); @@ -389,11 +386,11 @@ public class IdHelperService implements IIdHelperService { List andPredicates = new ArrayList<>(3); if (isNotBlank(next.getResourceType())) { - Predicate typeCriteria = cb.equal(from.get("myResourceType").as(String.class), next.getResourceType()); + Predicate typeCriteria = cb.equal(from.get("myResourceType"), next.getResourceType()); andPredicates.add(typeCriteria); } - Predicate idCriteria = cb.equal(from.get("myFhirId").as(String.class), next.getIdPart()); + Predicate idCriteria = cb.equal(from.get("myFhirId"), next.getIdPart()); andPredicates.add(idCriteria); getOptionalPartitionPredicate(theRequestPartitionId, cb, from).ifPresent(andPredicates::add); predicates.add(cb.and(andPredicates.toArray(EMPTY_PREDICATE_ARRAY))); @@ -431,19 +428,16 @@ public class IdHelperService implements IIdHelperService { if (myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()) { return Optional.empty(); } else if (theRequestPartitionId.isDefaultPartition() && myPartitionSettings.getDefaultPartitionId() == null) { - Predicate partitionIdCriteria = - cb.isNull(from.get("myPartitionIdValue").as(Integer.class)); + Predicate partitionIdCriteria = cb.isNull(from.get("myPartitionIdValue")); return Optional.of(partitionIdCriteria); } else if (!theRequestPartitionId.isAllPartitions()) { List partitionIds = theRequestPartitionId.getPartitionIds(); partitionIds = replaceDefaultPartitionIdIfNonNull(myPartitionSettings, partitionIds); if (partitionIds.size() > 1) { - Predicate partitionIdCriteria = - from.get("myPartitionIdValue").as(Integer.class).in(partitionIds); + Predicate partitionIdCriteria = from.get("myPartitionIdValue").in(partitionIds); return Optional.of(partitionIdCriteria); } else if (partitionIds.size() == 1) { - Predicate partitionIdCriteria = - cb.equal(from.get("myPartitionIdValue").as(Integer.class), partitionIds.get(0)); + Predicate partitionIdCriteria = cb.equal(from.get("myPartitionIdValue"), partitionIds.get(0)); return Optional.of(partitionIdCriteria); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java index c77d2e206ce..24b04c5e508 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java @@ -40,6 +40,9 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -49,9 +52,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Service @Lazy diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java index 2206f36f829..b915187a6f6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java @@ -38,6 +38,16 @@ import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Order; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.validation.constraints.NotNull; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.ObjectUtils; @@ -65,16 +75,6 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.validation.constraints.NotNull; import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_NAME; import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_PID_NAME; @@ -88,10 +88,10 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkDaoJpaImpl.class); @Autowired - IMdmLinkJpaRepository myMdmLinkDao; + protected EntityManager myEntityManager; @Autowired - protected EntityManager myEntityManager; + IMdmLinkJpaRepository myMdmLinkDao; @Autowired private IIdHelperService myIdHelperService; @@ -247,6 +247,8 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { @Override public Page search(MdmQuerySearchParameters theParams) { + Long totalResults = countTotalResults(theParams); + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(MdmLink.class); Root from = criteriaQuery.from(MdmLink.class); @@ -258,14 +260,9 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { if (!orderList.isEmpty()) { criteriaQuery.orderBy(orderList); } - TypedQuery typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); - CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); - countQuery.select(criteriaBuilder.count(countQuery.from(MdmLink.class))).where(finalQuery); - - Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult(); MdmPageRequest pageRequest = theParams.getPageRequest(); - + TypedQuery typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); List result = typedQuery .setFirstResult(pageRequest.getOffset()) .setMaxResults(pageRequest.getCount()) @@ -274,13 +271,26 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { return new PageImpl<>(result, PageRequest.of(pageRequest.getPage(), pageRequest.getCount()), totalResults); } + private Long countTotalResults(MdmQuerySearchParameters theParams) { + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); + Root from = countQuery.from(MdmLink.class); + + List andPredicates = buildPredicates(theParams, criteriaBuilder, from); + Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0])); + + countQuery.select(criteriaBuilder.count(from)).where(finalQuery); + + return myEntityManager.createQuery(countQuery).getSingleResult(); + } + @NotNull private List buildPredicates( MdmQuerySearchParameters theParams, CriteriaBuilder criteriaBuilder, Root from) { List andPredicates = new ArrayList<>(); if (theParams.getGoldenResourceId() != null) { Predicate goldenResourcePredicate = criteriaBuilder.equal( - from.get(GOLDEN_RESOURCE_PID_NAME).as(Long.class), + from.get(GOLDEN_RESOURCE_PID_NAME), (myIdHelperService.getPidOrThrowException( RequestPartitionId.allPartitions(), theParams.getGoldenResourceId())) .getId()); @@ -288,33 +298,31 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { } if (theParams.getSourceId() != null) { Predicate sourceIdPredicate = criteriaBuilder.equal( - from.get(SOURCE_PID_NAME).as(Long.class), + from.get(SOURCE_PID_NAME), (myIdHelperService.getPidOrThrowException( RequestPartitionId.allPartitions(), theParams.getSourceId())) .getId()); andPredicates.add(sourceIdPredicate); } if (theParams.getMatchResult() != null) { - Predicate matchResultPredicate = criteriaBuilder.equal( - from.get(MATCH_RESULT_NAME).as(MdmMatchResultEnum.class), theParams.getMatchResult()); + Predicate matchResultPredicate = + criteriaBuilder.equal(from.get(MATCH_RESULT_NAME), theParams.getMatchResult()); andPredicates.add(matchResultPredicate); } if (theParams.getLinkSource() != null) { - Predicate linkSourcePredicate = criteriaBuilder.equal( - from.get(LINK_SOURCE_NAME).as(MdmLinkSourceEnum.class), theParams.getLinkSource()); + Predicate linkSourcePredicate = + criteriaBuilder.equal(from.get(LINK_SOURCE_NAME), theParams.getLinkSource()); andPredicates.add(linkSourcePredicate); } if (!CollectionUtils.isEmpty(theParams.getPartitionIds())) { - Expression exp = - from.get(PARTITION_ID_NAME).get(PARTITION_ID_NAME).as(Integer.class); + Expression exp = from.get(PARTITION_ID_NAME).get(PARTITION_ID_NAME); Predicate linkSourcePredicate = exp.in(theParams.getPartitionIds()); andPredicates.add(linkSourcePredicate); } if (theParams.getResourceType() != null) { Predicate resourceTypePredicate = criteriaBuilder.equal( - from.get(GOLDEN_RESOURCE_NAME).get(RESOURCE_TYPE_NAME).as(String.class), - theParams.getResourceType()); + from.get(GOLDEN_RESOURCE_NAME).get(RESOURCE_TYPE_NAME), theParams.getResourceType()); andPredicates.add(resourceTypePredicate); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java index db20645a928..3bc7bd5ecfb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java @@ -29,15 +29,15 @@ import ca.uhn.fhir.mdm.model.MdmLinkMetrics; import ca.uhn.fhir.mdm.model.MdmLinkScoreMetrics; import ca.uhn.fhir.mdm.model.MdmMetrics; import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Query; import org.springframework.transaction.annotation.Transactional; import java.math.BigInteger; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Query; public class MdmMetricSvcJpaImpl extends BaseMdmMetricSvc { @@ -128,7 +128,7 @@ public class MdmMetricSvcJpaImpl extends BaseMdmMetricSvc { int length = row.length; for (int i = 0; i < length; i++) { // if there's nothing in the db, these values will all be null - BigInteger bi = row[i] != null ? (BigInteger) row[i] : BigInteger.valueOf(0); + Number bi = row[i] != null ? (Number) row[i] : BigInteger.valueOf(0); double bucket = getBucket(i); if (i == 0) { metrics.addScore(NULL_VALUE, bi.longValue()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 74bbcf58162..2ed5f0c2cca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java index 8867aa89d13..b950fe13981 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4b.model.Bundle; import org.hl7.fhir.r4b.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR4B extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java index 5925f9ee531..b1af9191e57 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR5 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java index eb87d87dcfd..f059361e7c6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.dao.search; +import org.hibernate.search.engine.search.predicate.SearchPredicate; import org.hibernate.search.engine.search.predicate.dsl.*; import org.hibernate.search.util.common.annotation.Incubating; @@ -108,80 +109,153 @@ class PathContext implements SearchPredicateFactory { // implement SearchPredicateFactory + @Override public MatchAllPredicateOptionsStep matchAll() { return myPredicateFactory.matchAll(); } + @Override + public MatchNonePredicateFinalStep matchNone() { + return myPredicateFactory.matchNone(); + } + + @Override public MatchIdPredicateMatchingStep id() { return myPredicateFactory.id(); } + @Override public BooleanPredicateClausesStep bool() { return myPredicateFactory.bool(); } + @Override public PredicateFinalStep bool(Consumer> clauseContributor) { return myPredicateFactory.bool(clauseContributor); } + @Override + public SimpleBooleanPredicateClausesStep and() { + return myPredicateFactory.and(); + } + + @Override + public SimpleBooleanPredicateOptionsStep and( + SearchPredicate theSearchPredicate, SearchPredicate... theSearchPredicates) { + return myPredicateFactory.and(theSearchPredicate, theSearchPredicates); + } + + @Override + public SimpleBooleanPredicateOptionsStep and( + PredicateFinalStep thePredicateFinalStep, PredicateFinalStep... thePredicateFinalSteps) { + return myPredicateFactory.and(thePredicateFinalStep, thePredicateFinalSteps); + } + + @Override + public SimpleBooleanPredicateClausesStep or() { + return myPredicateFactory.or(); + } + + @Override + public SimpleBooleanPredicateOptionsStep or( + SearchPredicate theSearchPredicate, SearchPredicate... theSearchPredicates) { + return myPredicateFactory.or(theSearchPredicate, theSearchPredicates); + } + + @Override + public SimpleBooleanPredicateOptionsStep or( + PredicateFinalStep thePredicateFinalStep, PredicateFinalStep... thePredicateFinalSteps) { + return myPredicateFactory.or(thePredicateFinalStep, thePredicateFinalSteps); + } + + @Override + public NotPredicateFinalStep not(SearchPredicate theSearchPredicate) { + return myPredicateFactory.not(theSearchPredicate); + } + + @Override + public NotPredicateFinalStep not(PredicateFinalStep thePredicateFinalStep) { + return myPredicateFactory.not(thePredicateFinalStep); + } + + @Override public MatchPredicateFieldStep match() { return myPredicateFactory.match(); } + @Override public RangePredicateFieldStep range() { return myPredicateFactory.range(); } + @Override public PhrasePredicateFieldStep phrase() { return myPredicateFactory.phrase(); } + @Override public WildcardPredicateFieldStep wildcard() { return myPredicateFactory.wildcard(); } + @Override public RegexpPredicateFieldStep regexp() { return myPredicateFactory.regexp(); } + @Override public TermsPredicateFieldStep terms() { return myPredicateFactory.terms(); } + @Override public NestedPredicateFieldStep nested() { return myPredicateFactory.nested(); } + @Override + public NestedPredicateClausesStep nested(String theObjectFieldPath) { + return myPredicateFactory.nested(theObjectFieldPath); + } + + @Override public SimpleQueryStringPredicateFieldStep simpleQueryString() { return myPredicateFactory.simpleQueryString(); } + @Override public ExistsPredicateFieldStep exists() { return myPredicateFactory.exists(); } + @Override public SpatialPredicateInitialStep spatial() { return myPredicateFactory.spatial(); } + @Override @Incubating public NamedPredicateOptionsStep named(String path) { return myPredicateFactory.named(path); } + @Override public T extension(SearchPredicateFactoryExtension extension) { return myPredicateFactory.extension(extension); } + @Override public SearchPredicateFactoryExtensionIfSupportedStep extension() { return myPredicateFactory.extension(); } + @Override @Incubating public SearchPredicateFactory withRoot(String objectFieldPath) { return myPredicateFactory.withRoot(objectFieldPath); } + @Override @Incubating public String toAbsolutePath(String relativeFieldPath) { return myPredicateFactory.toAbsolutePath(relativeFieldPath); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java index 1e52953446b..33a1932a48c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.delete; import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; import org.springframework.stereotype.Service; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; @Service public class DeleteConflictFinderService { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java index 9e7e7ff4474..9aee0268557 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.stream.Collectors; -import javax.persistence.EntityManager; public class DeleteExpungeSvcImpl implements IDeleteExpungeSvc { private static final Logger ourLog = LoggerFactory.getLogger(DeleteExpungeSvcImpl.class); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java index dd9205a8f30..311f201d141 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java @@ -21,24 +21,24 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.batch2.model.StatusEnum; +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.Date; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Lob; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH; import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java index e65c8814faa..159be9b503f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java @@ -20,27 +20,27 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; +import jakarta.persistence.Basic; +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.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.Date; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH; import static ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity.STATUS_MAX_LENGTH; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java index 7903eab9004..f564688c247 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java @@ -20,23 +20,23 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +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.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Version; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Version; /* * These classes are no longer needed. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java index 96ee1f1f8cc..0dcf6851d52 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java @@ -20,19 +20,19 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +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.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; /* * These classes are no longer needed. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java index a9bf341724a..8d70aeecbcd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java @@ -19,6 +19,20 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.r5.model.InstantType; @@ -27,20 +41,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; import static ca.uhn.fhir.rest.api.Constants.UUID_LENGTH; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java index f3151ea2ba4..7fff537777f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java @@ -22,22 +22,22 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobStatusEnum; import ca.uhn.fhir.jpa.bulk.imprt.model.JobFileRowProcessingModeEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; import static ca.uhn.fhir.rest.api.Constants.UUID_LENGTH; import static org.apache.commons.lang3.StringUtils.left; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java index 11a2998989f..00ee7169465 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java @@ -20,21 +20,21 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +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.SequenceGenerator; +import jakarta.persistence.Table; import java.io.Serializable; import java.nio.charset.StandardCharsets; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.left; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java index 9daa452e844..03417a1c66a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java @@ -19,6 +19,13 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.envers.RevisionEntity; import org.hibernate.envers.RevisionNumber; @@ -26,13 +33,6 @@ import org.hibernate.envers.RevisionTimestamp; import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; /** * This class exists strictly to override the default names used to generate Hibernate Envers revision table. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java index 4363bde74c5..3294240982c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java @@ -26,29 +26,31 @@ import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import com.fasterxml.jackson.annotation.JsonIgnore; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; import org.hibernate.envers.NotAudited; +import org.hibernate.type.SqlTypes; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; @Entity @Table( @@ -139,10 +141,12 @@ public class MdmLink extends AuditableBasePartitionable implements IMdmLink { } protected void init700() { + /* ************************************************ + * Start of 6.10 migrations + *********************************************** */ + Builder version = forVersion(VersionEnum.V7_0_0); // new indices on MdmLink diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java index 67af3c9a49d..8f8b0e4fe89 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java @@ -50,6 +50,15 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.ResourceUtil; import ca.uhn.fhir.util.StringUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.collections4.comparators.ReverseComparator; import org.apache.commons.lang3.Validate; import org.hl7.fhir.exceptions.FHIRException; @@ -85,15 +94,6 @@ import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.toPredicateArray; import static ca.uhn.fhir.util.StringUtil.toUtf8String; @@ -788,25 +788,23 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac Join resources = theRoot.join("myResources", JoinType.LEFT); - predicates.add(theCb.equal( - resources.get("myCanonicalUrl").as(String.class), thePackageSearchSpec.getResourceUrl())); + predicates.add(theCb.equal(resources.get("myCanonicalUrl"), thePackageSearchSpec.getResourceUrl())); } if (isNotBlank(thePackageSearchSpec.getDescription())) { String searchTerm = "%" + thePackageSearchSpec.getDescription() + "%"; searchTerm = StringUtil.normalizeStringForSearchIndexing(searchTerm); - predicates.add(theCb.like(theRoot.get("myDescriptionUpper").as(String.class), searchTerm)); + predicates.add(theCb.like(theRoot.get("myDescriptionUpper"), searchTerm)); } if (isNotBlank(thePackageSearchSpec.getFhirVersion())) { if (!thePackageSearchSpec.getFhirVersion().matches("([0-9]+\\.)+[0-9]+")) { FhirVersionEnum versionEnum = FhirVersionEnum.forVersionString(thePackageSearchSpec.getFhirVersion()); if (versionEnum != null) { - predicates.add(theCb.equal(theRoot.get("myFhirVersion").as(FhirVersionEnum.class), versionEnum)); + predicates.add(theCb.equal(theRoot.get("myFhirVersion").as(String.class), versionEnum.name())); } } else { - predicates.add(theCb.like( - theRoot.get("myFhirVersionId").as(String.class), thePackageSearchSpec.getFhirVersion() + "%")); + predicates.add(theCb.like(theRoot.get("myFhirVersionId"), thePackageSearchSpec.getFhirVersion() + "%")); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index 6b09789cca6..b38552017dd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.SearchParameterUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -69,7 +70,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.jpa.packages.util.PackageUtils.DEFAULT_INSTALL_TYPES; import static ca.uhn.fhir.util.SearchParameterUtil.getBaseAsStrings; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java index 60c04157046..655af28fcbc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ICallable; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +51,6 @@ import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java index 85b88f5f0bc..671649edb29 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -43,7 +44,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java index 42eb7b187ce..7e733249f96 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java @@ -44,7 +44,7 @@ public abstract class BaseJpaResourceProviderComposition */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IIdType theId, @Description( formalDefinition = @@ -76,7 +76,7 @@ public abstract class BaseJpaResourceProviderEncounter */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java index ef78d56c9e3..92d43b2ce75 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java @@ -40,7 +40,7 @@ public abstract class BaseJpaResourceProviderEncounterDstu2 extends BaseJpaResou */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @Description( formalDefinition = @@ -74,7 +74,7 @@ public abstract class BaseJpaResourceProviderEncounterDstu2 extends BaseJpaResou */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java index da112acefe2..f74a8e684d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java @@ -45,8 +45,8 @@ public abstract class BaseJpaResourceProviderObservation ex idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IIdType theId, @Description( shortDefinition = @@ -143,7 +143,7 @@ public abstract class BaseJpaResourceProviderPatient ex idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java index 4e5b15d8eab..d264abdca15 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.StopWatch; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.slf4j.Logger; @@ -42,7 +43,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; -import javax.servlet.http.HttpServletRequest; public abstract class BaseJpaSystemProvider extends BaseStorageSystemProvider implements IJpaSystemProvider { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java index dd06075d423..5cf700a423b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java @@ -44,12 +44,12 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ExtensionConstants; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu2.model.Subscription; import java.util.Collections; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java index 803254ea8bb..695544c4555 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java @@ -25,11 +25,10 @@ import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; - import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.endRequest; import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.startRequest; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index 85928273a71..88dc83093ec 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.util.ValidateUtil; import com.google.common.base.Charsets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; @@ -60,7 +61,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.*; @@ -446,6 +446,10 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { return retVal; } + public void setTerminologyLoaderSvc(ITermLoaderSvc theTermLoaderSvc) { + myTerminologyLoaderSvc = theTermLoaderSvc; + } + public static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor { private final File myNextFile; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java index 2bb327cf9c7..ba41fc11e68 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.ParametersUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -53,7 +54,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import java.util.Optional; import java.util.function.Supplier; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java index 0480f0290d2..5659c8129c4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.FhirTerser; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -35,7 +36,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; -import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderCodeSystem.applyVersionToSystem; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java index 2d971de810c..35a78056874 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ExtensionConstants; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; @@ -49,7 +50,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java index 4d09f9fed84..6ad2956d88d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java @@ -58,7 +58,7 @@ public class MemberMatchR4ResourceProvider { idempotent = false, returnParameters = {@OperationParam(name = "MemberIdentifier", typeName = "string")}) public Parameters patientMemberMatch( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition = "The target of the operation. Contain member Patient demographics.") @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) Patient theMemberPatient, diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java index a280d7e8293..b17cff10ace 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java @@ -60,68 +60,68 @@ public class HapiHSearchAnalysisConfigurers { theLuceneCtx .analyzer("autocompleteEdgeAnalyzer") .custom() - .tokenizer(PatternTokenizerFactory.class) - .param("pattern", "(.*)") - .param("group", "1") - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(EdgeNGramFilterFactory.class) + .tokenizer(PatternTokenizerFactory.NAME) + .param(PatternTokenizerFactory.PATTERN, "(.*)") + .param(PatternTokenizerFactory.GROUP, "1") + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(EdgeNGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "50"); theLuceneCtx .analyzer("autocompletePhoneticAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(PhoneticFilterFactory.class) - .param("encoder", "DoubleMetaphone") - .tokenFilter(SnowballPorterFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(PhoneticFilterFactory.NAME) + .param(PhoneticFilterFactory.ENCODER, "DoubleMetaphone") + .tokenFilter(SnowballPorterFilterFactory.NAME) .param("language", "English"); theLuceneCtx .analyzer("autocompleteNGramAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(WordDelimiterGraphFilterFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(NGramFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(WordDelimiterGraphFilterFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(NGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "20"); theLuceneCtx .analyzer("autocompleteWordEdgeAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(EdgeNGramFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(EdgeNGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "20"); theLuceneCtx .analyzer(STANDARD_ANALYZER) .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); theLuceneCtx .analyzer(NORM_STRING_ANALYZER) .custom() - .tokenizer(KeywordTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenizer(KeywordTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); - theLuceneCtx.analyzer(EXACT_ANALYZER).custom().tokenizer(KeywordTokenizerFactory.class); + theLuceneCtx.analyzer(EXACT_ANALYZER).custom().tokenizer(KeywordTokenizerFactory.NAME); - theLuceneCtx.analyzer("conceptParentPidsAnalyzer").custom().tokenizer(WhitespaceTokenizerFactory.class); + theLuceneCtx.analyzer("conceptParentPidsAnalyzer").custom().tokenizer(WhitespaceTokenizerFactory.NAME); theLuceneCtx .normalizer(LOWERCASE_ASCIIFOLDING_NORMALIZER) .custom() - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index 51a994e7afa..b25435b6751 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -58,6 +58,8 @@ import ca.uhn.fhir.rest.server.method.ResponsePage; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,8 +72,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; public class PersistedJpaBundleProvider implements IBundleProvider { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java index ee380067c7e..47512f96648 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java @@ -26,13 +26,13 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import jakarta.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; -import javax.persistence.EntityManager; /** * This service ensures uniqueness of resources during create or create-on-update by storing the resource searchUrl. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java index 89f77e51b14..8d2a9367820 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java @@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.ServerInterceptorUtil; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; +import jakarta.persistence.EntityManager; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; @@ -55,7 +56,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.persistence.EntityManager; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantCount; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantOnlyCount; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index 88f5f75a87e..bc40f4c3c19 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -95,6 +95,13 @@ import ca.uhn.fhir.util.StringUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.collect.Streams; import com.healthmarketscience.sqlbuilder.Condition; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.math.NumberUtils; @@ -121,13 +128,6 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; import static ca.uhn.fhir.jpa.model.util.JpaConstants.UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE; import static ca.uhn.fhir.jpa.search.builder.QueryStack.LOCATION_POSITION; @@ -495,15 +495,8 @@ public class SearchBuilder implements ISearchBuilder { myRequestPartitionId, myResourceName, String.valueOf(lastNResourceId))) .collect(Collectors.toList()); } else { - if (myIElasticsearchSvc == null) { - throw new InvalidRequestException(Msg.code(2033) - + "LastN operation is not enabled on this service, can not process this request"); - } - // use the dedicated observation ES/Lucene index to support lastN query - return myIElasticsearchSvc.executeLastN(myParams, myContext, theMaximumResults).stream() - .map(lastnResourceId -> myIdHelperService.resolveResourcePersistentIds( - myRequestPartitionId, myResourceName, lastnResourceId)) - .collect(Collectors.toList()); + throw new InvalidRequestException( + Msg.code(2033) + "LastN operation is not enabled on this service, can not process this request"); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java index 78b7f115ab6..4f427431418 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java @@ -33,10 +33,10 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import jakarta.persistence.criteria.CriteriaBuilder; import org.springframework.beans.factory.annotation.Autowired; import java.math.BigDecimal; -import javax.persistence.criteria.CriteriaBuilder; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java index e840bdec9d3..3a310e5c4d7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java @@ -64,7 +64,9 @@ import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.pagination.AbstractLimitHandler; -import org.hibernate.engine.spi.RowSelection; +import org.hibernate.query.internal.QueryOptionsImpl; +import org.hibernate.query.spi.Limit; +import org.hibernate.query.spi.QueryOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -501,10 +503,11 @@ public class SearchQueryBuilder { maxResultsToFetch = defaultIfNull(maxResultsToFetch, 10000); AbstractLimitHandler limitHandler = (AbstractLimitHandler) myDialect.getLimitHandler(); - RowSelection selection = new RowSelection(); + Limit selection = new Limit(); selection.setFirstRow(offset); selection.setMaxRows(maxResultsToFetch); - sql = limitHandler.processSql(sql, selection); + QueryOptions queryOptions = new QueryOptionsImpl(); + sql = limitHandler.processSql(sql, selection, queryOptions); int startOfQueryParameterIndex = 0; @@ -517,14 +520,14 @@ public class SearchQueryBuilder { if (sql.contains("top(?)")) { bindVariables.add(0, maxResultsToFetch); } - if (sql.contains("offset 0 rows fetch next ? rows only")) { + if (sql.contains("offset 0 rows fetch first ? rows only")) { bindVariables.add(maxResultsToFetch); } if (sql.contains("offset ? rows fetch next ? rows only")) { bindVariables.add(theOffset); bindVariables.add(maxResultsToFetch); } - if (offset != null && sql.contains("__row__")) { + if (offset != null && sql.contains("rownumber_")) { bindVariables.add(theOffset + 1); bindVariables.add(theOffset + maxResultsToFetch + 1); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java index 8f8d4600ee5..d1edddeda4f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java @@ -25,6 +25,11 @@ import ca.uhn.fhir.jpa.search.builder.ISearchQueryExecutor; import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.IoUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; import org.apache.commons.lang3.Validate; import org.hibernate.CacheMode; import org.hibernate.ScrollMode; @@ -33,11 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; -import javax.persistence.EntityManager; -import javax.persistence.FlushModeType; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; +import java.util.Objects; public class SearchQueryExecutor implements ISearchQueryExecutor { @@ -51,7 +52,7 @@ public class SearchQueryExecutor implements ISearchQueryExecutor { private EntityManager myEntityManager; private boolean myQueryInitialized; - private ScrollableResultsIterator myResultSet; + private ScrollableResultsIterator myResultSet; private Long myNext; /** @@ -144,7 +145,13 @@ public class SearchQueryExecutor implements ISearchQueryExecutor { if (myResultSet == null || !myResultSet.hasNext()) { myNext = NO_MORE; } else { - Number next = myResultSet.next(); + Object nextRow = Objects.requireNonNull(myResultSet.next()); + Number next; + if (nextRow instanceof Number) { + next = (Number) nextRow; + } else { + next = (Number) ((Object[]) nextRow)[0]; + } myNext = next.longValue(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java index 9396f7d9542..700d409e216 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java @@ -57,7 +57,6 @@ import co.elastic.apm.api.Transaction; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import java.io.IOException; @@ -447,7 +446,6 @@ public class SearchTask implements Callable { myTxService .withRequest(myRequest) .withRequestPartitionId(myRequestPartitionId) - .withIsolation(Isolation.READ_COMMITTED) .execute(() -> doSearch()); mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java index 0f666f60505..1e886ca9c1b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java @@ -33,6 +33,7 @@ import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.EntityManager; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; import org.hibernate.Session; @@ -53,7 +54,6 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { /* diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java index a5c8481b8a5..564d39bf804 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java @@ -21,6 +21,10 @@ package ca.uhn.fhir.jpa.search.lastn; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHost; @@ -32,7 +36,6 @@ import org.apache.http.message.BasicHeader; import org.elasticsearch.client.Node; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import java.util.Arrays; import java.util.List; @@ -41,7 +44,7 @@ import javax.annotation.Nullable; public class ElasticsearchRestClientFactory { - public static RestHighLevelClient createElasticsearchHighLevelRestClient( + public static ElasticsearchClient createElasticsearchHighLevelRestClient( String protocol, String hosts, @Nullable String theUsername, @Nullable String thePassword) { if (hosts.contains("://")) { @@ -78,6 +81,12 @@ public class ElasticsearchRestClientFactory { Header[] defaultHeaders = new Header[] {new BasicHeader("Content-Type", "application/json")}; clientBuilder.setDefaultHeaders(defaultHeaders); - return new RestHighLevelClient(clientBuilder); + RestClient restClient = clientBuilder.build(); + + // Create the transport with a Jackson mapper + ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + + // And create the API client + return new ElasticsearchClient(transport); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java index 1479b017cd8..f9d344277d4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java @@ -23,108 +23,41 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.TolerantJsonParser; import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper; -import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch.core.SearchRequest; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.Validate; -import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.indices.CreateIndexRequest; -import org.elasticsearch.client.indices.CreateIndexResponse; -import org.elasticsearch.client.indices.GetIndexRequest; -import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.MatchQueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.RangeQueryBuilder; -import org.elasticsearch.index.reindex.DeleteByQueryRequest; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.SearchHits; -import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregationBuilders; -import org.elasticsearch.search.aggregations.Aggregations; -import org.elasticsearch.search.aggregations.BucketOrder; -import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; -import org.elasticsearch.search.aggregations.bucket.composite.ParsedComposite; -import org.elasticsearch.search.aggregations.bucket.composite.TermsValuesSourceBuilder; -import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.elasticsearch.search.aggregations.metrics.ParsedTopHits; -import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.sort.SortOrder; -import org.elasticsearch.xcontent.XContentType; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; +import java.io.StringReader; import java.util.Collection; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; -import static org.apache.commons.lang3.StringUtils.isBlank; - public class ElasticsearchSvcImpl implements IElasticsearchSvc { - private static final Logger ourLog = LoggerFactory.getLogger(ElasticsearchSvcImpl.class); - // Index Constants public static final String OBSERVATION_INDEX = "observation_index"; public static final String OBSERVATION_CODE_INDEX = "code_index"; - public static final String OBSERVATION_DOCUMENT_TYPE = - "ca.uhn.fhir.jpa.model.entity.ObservationIndexedSearchParamLastNEntity"; - public static final String CODE_DOCUMENT_TYPE = - "ca.uhn.fhir.jpa.model.entity.ObservationIndexedCodeCodeableConceptEntity"; public static final String OBSERVATION_INDEX_SCHEMA_FILE = "ObservationIndexSchema.json"; public static final String OBSERVATION_CODE_INDEX_SCHEMA_FILE = "ObservationCodeIndexSchema.json"; // Aggregation Constants - private static final String GROUP_BY_SUBJECT = "group_by_subject"; - private static final String GROUP_BY_SYSTEM = "group_by_system"; - private static final String GROUP_BY_CODE = "group_by_code"; - private static final String MOST_RECENT_EFFECTIVE = "most_recent_effective"; // Observation index document element names private static final String OBSERVATION_IDENTIFIER_FIELD_NAME = "identifier"; - private static final String OBSERVATION_SUBJECT_FIELD_NAME = "subject"; - private static final String OBSERVATION_CODEVALUE_FIELD_NAME = "codeconceptcodingcode"; - private static final String OBSERVATION_CODESYSTEM_FIELD_NAME = "codeconceptcodingsystem"; - private static final String OBSERVATION_CODEHASH_FIELD_NAME = "codeconceptcodingcode_system_hash"; - private static final String OBSERVATION_CODEDISPLAY_FIELD_NAME = "codeconceptcodingdisplay"; - private static final String OBSERVATION_CODE_TEXT_FIELD_NAME = "codeconcepttext"; - private static final String OBSERVATION_EFFECTIVEDTM_FIELD_NAME = "effectivedtm"; - private static final String OBSERVATION_CATEGORYHASH_FIELD_NAME = "categoryconceptcodingcode_system_hash"; - private static final String OBSERVATION_CATEGORYVALUE_FIELD_NAME = "categoryconceptcodingcode"; - private static final String OBSERVATION_CATEGORYSYSTEM_FIELD_NAME = "categoryconceptcodingsystem"; - private static final String OBSERVATION_CATEGORYDISPLAY_FIELD_NAME = "categoryconceptcodingdisplay"; - private static final String OBSERVATION_CATEGORYTEXT_FIELD_NAME = "categoryconcepttext"; // Code index document element names private static final String CODE_HASH = "codingcode_system_hash"; @@ -132,12 +65,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { private static final String OBSERVATION_RESOURCE_NAME = "Observation"; - private final RestHighLevelClient myRestHighLevelClient; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Autowired - private PartitionSettings myPartitionSettings; + private final ElasticsearchClient myRestHighLevelClient; @Autowired private FhirContext myContext; @@ -150,11 +78,11 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { @Nullable String theUsername, @Nullable String thePassword) { this(theProtocol, theHostname, theUsername, thePassword); - this.myPartitionSettings = thePartitionSetings; } public ElasticsearchSvcImpl( String theProtocol, String theHostname, @Nullable String theUsername, @Nullable String thePassword) { + myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( theProtocol, theHostname, theUsername, thePassword); @@ -200,648 +128,30 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { } private boolean createIndex(String theIndexName, String theMapping) throws IOException { - CreateIndexRequest request = new CreateIndexRequest(theIndexName); - request.source(theMapping, XContentType.JSON); - CreateIndexResponse createIndexResponse = - myRestHighLevelClient.indices().create(request, RequestOptions.DEFAULT); - return createIndexResponse.isAcknowledged(); + return myRestHighLevelClient + .indices() + .create(cir -> cir.index(theIndexName).withJson(new StringReader(theMapping))) + .acknowledged(); } private boolean indexExists(String theIndexName) throws IOException { - GetIndexRequest request = new GetIndexRequest(theIndexName); - return myRestHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); - } - - @Override - public List executeLastN( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch) { - Validate.isTrue( - !myPartitionSettings.isPartitioningEnabled(), - "$lastn is not currently supported on partitioned servers"); - - String[] topHitsInclude = {OBSERVATION_IDENTIFIER_FIELD_NAME}; - return buildAndExecuteSearch( - theSearchParameterMap, - theFhirContext, - topHitsInclude, - ObservationJson::getIdentifier, - theMaxResultsToFetch); - } - - private List buildAndExecuteSearch( - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - String[] topHitsInclude, - Function setValue, - Integer theMaxResultsToFetch) { - String patientParamName = LastNParameterHelper.getPatientParamName(theFhirContext); - String subjectParamName = LastNParameterHelper.getSubjectParamName(theFhirContext); - List searchResults = new ArrayList<>(); - if (theSearchParameterMap.containsKey(patientParamName) - || theSearchParameterMap.containsKey(subjectParamName)) { - for (String subject : - getSubjectReferenceCriteria(patientParamName, subjectParamName, theSearchParameterMap)) { - if (theMaxResultsToFetch != null && searchResults.size() >= theMaxResultsToFetch) { - break; - } - SearchRequest myLastNRequest = buildObservationsSearchRequest( - subject, - theSearchParameterMap, - theFhirContext, - createObservationSubjectAggregationBuilder( - getMaxParameter(theSearchParameterMap), topHitsInclude)); - ourLog.debug("ElasticSearch query: {}", myLastNRequest.source().toString()); - try { - SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); - searchResults.addAll(buildObservationList( - lastnResponse, setValue, theSearchParameterMap, theFhirContext, theMaxResultsToFetch)); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1178) + "Unable to execute LastN request", theE); - } - } - } else { - SearchRequest myLastNRequest = buildObservationsSearchRequest( - theSearchParameterMap, - theFhirContext, - createObservationCodeAggregationBuilder(getMaxParameter(theSearchParameterMap), topHitsInclude)); - ourLog.debug("ElasticSearch query: {}", myLastNRequest.source().toString()); - try { - SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); - searchResults.addAll(buildObservationList( - lastnResponse, setValue, theSearchParameterMap, theFhirContext, theMaxResultsToFetch)); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1179) + "Unable to execute LastN request", theE); - } - } - return searchResults; - } - - private int getMaxParameter(SearchParameterMap theSearchParameterMap) { - if (theSearchParameterMap.getLastNMax() == null) { - return 1; - } else { - return theSearchParameterMap.getLastNMax(); - } - } - - private List getSubjectReferenceCriteria( - String thePatientParamName, String theSubjectParamName, SearchParameterMap theSearchParameterMap) { - List subjectReferenceCriteria = new ArrayList<>(); - - List> patientParams = new ArrayList<>(); - if (theSearchParameterMap.get(thePatientParamName) != null) { - patientParams.addAll(theSearchParameterMap.get(thePatientParamName)); - } - if (theSearchParameterMap.get(theSubjectParamName) != null) { - patientParams.addAll(theSearchParameterMap.get(theSubjectParamName)); - } - for (List nextSubjectList : patientParams) { - subjectReferenceCriteria.addAll(getReferenceValues(nextSubjectList)); - } - return subjectReferenceCriteria; - } - - private List getReferenceValues(List referenceParams) { - List referenceList = new ArrayList<>(); - - for (IQueryParameterType nextOr : referenceParams) { - - if (nextOr instanceof ReferenceParam) { - ReferenceParam ref = (ReferenceParam) nextOr; - if (isBlank(ref.getChain())) { - referenceList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1180) + "Invalid token type (expecting ReferenceParam): " + nextOr.getClass()); - } - } - return referenceList; - } - - private CompositeAggregationBuilder createObservationSubjectAggregationBuilder( - Integer theMaxNumberObservationsPerCode, String[] theTopHitsInclude) { - CompositeValuesSourceBuilder subjectValuesBuilder = - new TermsValuesSourceBuilder(OBSERVATION_SUBJECT_FIELD_NAME).field(OBSERVATION_SUBJECT_FIELD_NAME); - List> compositeAggSubjectSources = new ArrayList<>(); - compositeAggSubjectSources.add(subjectValuesBuilder); - CompositeAggregationBuilder compositeAggregationSubjectBuilder = - new CompositeAggregationBuilder(GROUP_BY_SUBJECT, compositeAggSubjectSources); - compositeAggregationSubjectBuilder.subAggregation( - createObservationCodeAggregationBuilder(theMaxNumberObservationsPerCode, theTopHitsInclude)); - compositeAggregationSubjectBuilder.size(10000); - - return compositeAggregationSubjectBuilder; - } - - private TermsAggregationBuilder createObservationCodeAggregationBuilder( - int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) { - TermsAggregationBuilder observationCodeCodeAggregationBuilder = - new TermsAggregationBuilder(GROUP_BY_CODE).field(OBSERVATION_CODEVALUE_FIELD_NAME); - observationCodeCodeAggregationBuilder.order(BucketOrder.key(true)); - // Top Hits Aggregation - observationCodeCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits(MOST_RECENT_EFFECTIVE) - .sort(OBSERVATION_EFFECTIVEDTM_FIELD_NAME, SortOrder.DESC) - .fetchSource(theTopHitsInclude, null) - .size(theMaxNumberObservationsPerCode)); - observationCodeCodeAggregationBuilder.size(10000); - TermsAggregationBuilder observationCodeSystemAggregationBuilder = - new TermsAggregationBuilder(GROUP_BY_SYSTEM).field(OBSERVATION_CODESYSTEM_FIELD_NAME); - observationCodeSystemAggregationBuilder.order(BucketOrder.key(true)); - observationCodeSystemAggregationBuilder.subAggregation(observationCodeCodeAggregationBuilder); - return observationCodeSystemAggregationBuilder; - } - - private SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException { - return myRestHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); - } - - private List buildObservationList( - SearchResponse theSearchResponse, - Function setValue, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - Integer theMaxResultsToFetch) - throws IOException { - List theObservationList = new ArrayList<>(); - if (theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext))) { - for (ParsedComposite.ParsedBucket subjectBucket : getSubjectBuckets(theSearchResponse)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(subjectBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - String indexedObservation = lastNMatch.getSourceAsString(); - ObservationJson observationJson = - objectMapper.readValue(indexedObservation, ObservationJson.class); - theObservationList.add(setValue.apply(observationJson)); - } - } - } - } else { - for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(theSearchResponse)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - String indexedObservation = lastNMatch.getSourceAsString(); - ObservationJson observationJson = objectMapper.readValue(indexedObservation, ObservationJson.class); - theObservationList.add(setValue.apply(observationJson)); - } - } - } - - return theObservationList; - } - - private List getSubjectBuckets(SearchResponse theSearchResponse) { - Aggregations responseAggregations = theSearchResponse.getAggregations(); - ParsedComposite aggregatedSubjects = responseAggregations.get(GROUP_BY_SUBJECT); - return aggregatedSubjects.getBuckets(); - } - - private List getObservationCodeBuckets(SearchResponse theSearchResponse) { - Aggregations responseAggregations = theSearchResponse.getAggregations(); - return getObservationCodeBuckets(responseAggregations); - } - - private List getObservationCodeBuckets(ParsedComposite.ParsedBucket theSubjectBucket) { - Aggregations observationCodeSystemAggregations = theSubjectBucket.getAggregations(); - return getObservationCodeBuckets(observationCodeSystemAggregations); - } - - private List getObservationCodeBuckets(Aggregations theObservationCodeSystemAggregations) { - List retVal = new ArrayList<>(); - ParsedTerms aggregatedObservationCodeSystems = theObservationCodeSystemAggregations.get(GROUP_BY_SYSTEM); - for (Terms.Bucket observationCodeSystem : aggregatedObservationCodeSystems.getBuckets()) { - Aggregations observationCodeCodeAggregations = observationCodeSystem.getAggregations(); - ParsedTerms aggregatedObservationCodeCodes = observationCodeCodeAggregations.get(GROUP_BY_CODE); - retVal.addAll(aggregatedObservationCodeCodes.getBuckets()); - } - return retVal; - } - - private SearchHit[] getLastNMatches(Terms.Bucket theObservationCodeBucket) { - Aggregations topHitObservationCodes = theObservationCodeBucket.getAggregations(); - ParsedTopHits parsedTopHits = topHitObservationCodes.get(MOST_RECENT_EFFECTIVE); - return parsedTopHits.getHits().getHits(); - } - - private SearchRequest buildObservationsSearchRequest( - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - AggregationBuilder theAggregationBuilder) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - if (!searchParamsHaveLastNCriteria(theSearchParameterMap, theFhirContext)) { - searchSourceBuilder.query(QueryBuilders.matchAllQuery()); - } else { - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - searchSourceBuilder.query(boolQueryBuilder); - } - searchSourceBuilder.size(0); - - // Aggregation by order codes - searchSourceBuilder.aggregation(theAggregationBuilder); - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - private SearchRequest buildObservationsSearchRequest( - String theSubjectParam, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - AggregationBuilder theAggregationBuilder) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.must(QueryBuilders.termQuery(OBSERVATION_SUBJECT_FIELD_NAME, theSubjectParam)); - addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(0); - - // Aggregation by order codes - searchSourceBuilder.aggregation(theAggregationBuilder); - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - private Boolean searchParamsHaveLastNCriteria( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { - return theSearchParameterMap != null - && (theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getCategoryParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getCodeParamName(theFhirContext))); - } - - private void addCategoriesCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String categoryParamName = LastNParameterHelper.getCategoryParamName(theFhirContext); - if (theSearchParameterMap.containsKey(categoryParamName)) { - ArrayList codeSystemHashList = new ArrayList<>(); - ArrayList codeOnlyList = new ArrayList<>(); - ArrayList systemOnlyList = new ArrayList<>(); - ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(categoryParamName); - for (List nextAnd : andOrParams) { - codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd)); - codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd)); - systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd)); - textOnlyList.addAll(getCodingTextOnlyValues(nextAnd)); - } - if (codeSystemHashList.size() > 0) { - theBoolQueryBuilder.must( - QueryBuilders.termsQuery(OBSERVATION_CATEGORYHASH_FIELD_NAME, codeSystemHashList)); - } - if (codeOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CATEGORYVALUE_FIELD_NAME, codeOnlyList)); - } - if (systemOnlyList.size() > 0) { - theBoolQueryBuilder.must( - QueryBuilders.termsQuery(OBSERVATION_CATEGORYSYSTEM_FIELD_NAME, systemOnlyList)); - } - if (textOnlyList.size() > 0) { - BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery(); - for (String textOnlyParam : textOnlyList) { - myTextBoolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery( - OBSERVATION_CATEGORYDISPLAY_FIELD_NAME, textOnlyParam)); - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CATEGORYTEXT_FIELD_NAME, textOnlyParam)); - } - theBoolQueryBuilder.must(myTextBoolQueryBuilder); - } - } - } - - private List getCodingCodeSystemValues(List codeParams) { - ArrayList codeSystemHashList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getSystem() != null && ref.getValue() != null) { - codeSystemHashList.add( - String.valueOf(CodeSystemHash.hashCodeSystem(ref.getSystem(), ref.getValue()))); - } - } else { - throw new IllegalArgumentException( - Msg.code(1181) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return codeSystemHashList; - } - - private List getCodingCodeOnlyValues(List codeParams) { - ArrayList codeOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getValue() != null && ref.getSystem() == null && !ref.isText()) { - codeOnlyList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1182) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return codeOnlyList; - } - - private List getCodingSystemOnlyValues(List codeParams) { - ArrayList systemOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getValue() == null && ref.getSystem() != null) { - systemOnlyList.add(ref.getSystem()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1183) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return systemOnlyList; - } - - private List getCodingTextOnlyValues(List codeParams) { - ArrayList textOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.isText() && ref.getValue() != null) { - textOnlyList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1184) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return textOnlyList; - } - - private void addObservationCodeCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String codeParamName = LastNParameterHelper.getCodeParamName(theFhirContext); - if (theSearchParameterMap.containsKey(codeParamName)) { - ArrayList codeSystemHashList = new ArrayList<>(); - ArrayList codeOnlyList = new ArrayList<>(); - ArrayList systemOnlyList = new ArrayList<>(); - ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(codeParamName); - for (List nextAnd : andOrParams) { - codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd)); - codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd)); - systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd)); - textOnlyList.addAll(getCodingTextOnlyValues(nextAnd)); - } - if (codeSystemHashList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODEHASH_FIELD_NAME, codeSystemHashList)); - } - if (codeOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODEVALUE_FIELD_NAME, codeOnlyList)); - } - if (systemOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODESYSTEM_FIELD_NAME, systemOnlyList)); - } - if (textOnlyList.size() > 0) { - BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery(); - for (String textOnlyParam : textOnlyList) { - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CODEDISPLAY_FIELD_NAME, textOnlyParam)); - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CODE_TEXT_FIELD_NAME, textOnlyParam)); - } - theBoolQueryBuilder.must(myTextBoolQueryBuilder); - } - } - } - - private void addDateCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String dateParamName = LastNParameterHelper.getEffectiveParamName(theFhirContext); - if (theSearchParameterMap.containsKey(dateParamName)) { - List> andOrParams = theSearchParameterMap.get(dateParamName); - for (List nextAnd : andOrParams) { - BoolQueryBuilder myDateBoolQueryBuilder = new BoolQueryBuilder(); - for (IQueryParameterType nextOr : nextAnd) { - if (nextOr instanceof DateParam) { - DateParam myDate = (DateParam) nextOr; - createDateCriteria(myDate, myDateBoolQueryBuilder); - } - } - theBoolQueryBuilder.must(myDateBoolQueryBuilder); - } - } - } - - private void createDateCriteria(DateParam theDate, BoolQueryBuilder theBoolQueryBuilder) { - Long dateInstant = theDate.getValue().getTime(); - RangeQueryBuilder myRangeQueryBuilder = new RangeQueryBuilder(OBSERVATION_EFFECTIVEDTM_FIELD_NAME); - - ParamPrefixEnum prefix = theDate.getPrefix(); - if (prefix == ParamPrefixEnum.GREATERTHAN || prefix == ParamPrefixEnum.STARTS_AFTER) { - theBoolQueryBuilder.should(myRangeQueryBuilder.gt(dateInstant)); - } else if (prefix == ParamPrefixEnum.LESSTHAN || prefix == ParamPrefixEnum.ENDS_BEFORE) { - theBoolQueryBuilder.should(myRangeQueryBuilder.lt(dateInstant)); - } else if (prefix == ParamPrefixEnum.LESSTHAN_OR_EQUALS) { - theBoolQueryBuilder.should(myRangeQueryBuilder.lte(dateInstant)); - } else if (prefix == ParamPrefixEnum.GREATERTHAN_OR_EQUALS) { - theBoolQueryBuilder.should(myRangeQueryBuilder.gte(dateInstant)); - } else { - theBoolQueryBuilder.should(new MatchQueryBuilder(OBSERVATION_EFFECTIVEDTM_FIELD_NAME, dateInstant)); - } - } - - @VisibleForTesting - public List executeLastNWithAllFieldsForTest( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { - return buildAndExecuteSearch(theSearchParameterMap, theFhirContext, null, t -> t, 100); - } - - @VisibleForTesting - List queryAllIndexedObservationCodesForTest() throws IOException { - SearchRequest codeSearchRequest = new SearchRequest(OBSERVATION_CODE_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - searchSourceBuilder.query(QueryBuilders.matchAllQuery()); - searchSourceBuilder.size(1000); - codeSearchRequest.source(searchSourceBuilder); - SearchResponse codeSearchResponse = executeSearchRequest(codeSearchRequest); - return buildCodeResult(codeSearchResponse); - } - - private List buildCodeResult(SearchResponse theSearchResponse) throws JsonProcessingException { - SearchHits codeHits = theSearchResponse.getHits(); - List codes = new ArrayList<>(); - for (SearchHit codeHit : codeHits) { - CodeJson code = objectMapper.readValue(codeHit.getSourceAsString(), CodeJson.class); - codes.add(code); - } - return codes; - } - - @Override - public ObservationJson getObservationDocument(String theDocumentID) { - if (theDocumentID == null) { - throw new InvalidRequestException( - Msg.code(1185) + "Require non-null document ID for observation document query"); - } - SearchRequest theSearchRequest = buildSingleObservationSearchRequest(theDocumentID); - ObservationJson observationDocumentJson = null; - try { - SearchResponse observationDocumentResponse = executeSearchRequest(theSearchRequest); - SearchHit[] observationDocumentHits = - observationDocumentResponse.getHits().getHits(); - if (observationDocumentHits.length > 0) { - // There should be no more than one hit for the identifier - String observationDocument = observationDocumentHits[0].getSourceAsString(); - observationDocumentJson = objectMapper.readValue(observationDocument, ObservationJson.class); - } - - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1186) + "Unable to execute observation document query for ID " + theDocumentID, theE); - } - - return observationDocumentJson; - } - - private SearchRequest buildSingleObservationSearchRequest(String theObservationIdentifier) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.must(QueryBuilders.termQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, theObservationIdentifier)); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(1); - - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - @Override - public CodeJson getObservationCodeDocument(String theCodeSystemHash, String theText) { - if (theCodeSystemHash == null && theText == null) { - throw new InvalidRequestException(Msg.code(1187) - + "Require a non-null code system hash value or display value for observation code document query"); - } - SearchRequest theSearchRequest = buildSingleObservationCodeSearchRequest(theCodeSystemHash, theText); - CodeJson observationCodeDocumentJson = null; - try { - SearchResponse observationCodeDocumentResponse = executeSearchRequest(theSearchRequest); - SearchHit[] observationCodeDocumentHits = - observationCodeDocumentResponse.getHits().getHits(); - if (observationCodeDocumentHits.length > 0) { - // There should be no more than one hit for the code lookup. - String observationCodeDocument = observationCodeDocumentHits[0].getSourceAsString(); - observationCodeDocumentJson = objectMapper.readValue(observationCodeDocument, CodeJson.class); - } - - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1188) + "Unable to execute observation code document query hash code or display", theE); - } - - return observationCodeDocumentJson; - } - - private SearchRequest buildSingleObservationCodeSearchRequest(String theCodeSystemHash, String theText) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_CODE_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - if (theCodeSystemHash != null) { - boolQueryBuilder.must(QueryBuilders.termQuery(CODE_HASH, theCodeSystemHash)); - } else { - boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(CODE_TEXT, theText)); - } - - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(1); - - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - @Override - public Boolean createOrUpdateObservationIndex(String theDocumentId, ObservationJson theObservationDocument) { - try { - String documentToIndex = objectMapper.writeValueAsString(theObservationDocument); - return performIndex( - OBSERVATION_INDEX, theDocumentId, documentToIndex, ElasticsearchSvcImpl.OBSERVATION_DOCUMENT_TYPE); - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1189) + "Unable to persist Observation document " + theDocumentId); - } - } - - @Override - public Boolean createOrUpdateObservationCodeIndex( - String theCodeableConceptID, CodeJson theObservationCodeDocument) { - try { - String documentToIndex = objectMapper.writeValueAsString(theObservationCodeDocument); - return performIndex( - OBSERVATION_CODE_INDEX, - theCodeableConceptID, - documentToIndex, - ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE); - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1190) + "Unable to persist Observation Code document " + theCodeableConceptID); - } - } - - private boolean performIndex( - String theIndexName, String theDocumentId, String theIndexDocument, String theDocumentType) - throws IOException { - IndexResponse indexResponse = myRestHighLevelClient.index( - createIndexRequest(theIndexName, theDocumentId, theIndexDocument, theDocumentType), - RequestOptions.DEFAULT); - - return (indexResponse.getResult() == DocWriteResponse.Result.CREATED) - || (indexResponse.getResult() == DocWriteResponse.Result.UPDATED); + ExistsRequest request = new ExistsRequest.Builder().index(theIndexName).build(); + return myRestHighLevelClient.indices().exists(request).value(); } @Override public void close() throws IOException { - myRestHighLevelClient.close(); + // nothing } @Override public List getObservationResources(Collection thePids) { SearchRequest searchRequest = buildObservationResourceSearchRequest(thePids); try { - SearchResponse observationDocumentResponse = executeSearchRequest(searchRequest); - SearchHit[] observationDocumentHits = - observationDocumentResponse.getHits().getHits(); + SearchResponse observationDocumentResponse = + myRestHighLevelClient.search(searchRequest, ObservationJson.class); + List> observationDocumentHits = + observationDocumentResponse.hits().hits(); IParser parser = TolerantJsonParser.createWithLenientErrorHandling(myContext, null); Class resourceType = myContext.getResourceDefinition(OBSERVATION_RESOURCE_NAME).getImplementingClass(); @@ -849,8 +159,8 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { * @see ca.uhn.fhir.jpa.dao.BaseHapiFhirDao#toResource(Class, IBaseResourceEntity, Collection, boolean) for * details about parsing raw json to BaseResource */ - return Arrays.stream(observationDocumentHits) - .map(this::parseObservationJson) + return observationDocumentHits.stream() + .map(Hit::source) .map(observationJson -> parser.parseResource(resourceType, observationJson.getResource())) .collect(Collectors.toList()); } catch (IOException theE) { @@ -859,55 +169,23 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { } } - private ObservationJson parseObservationJson(SearchHit theSearchHit) { - try { - return objectMapper.readValue(theSearchHit.getSourceAsString(), ObservationJson.class); - } catch (JsonProcessingException exp) { - throw new InvalidRequestException(Msg.code(2004) + "Unable to parse the observation resource json", exp); - } - } - private SearchRequest buildObservationResourceSearchRequest(Collection thePids) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - List pidParams = thePids.stream().map(Object::toString).collect(Collectors.toList()); - boolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, pidParams)); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(thePids.size()); - searchRequest.source(searchSourceBuilder); - return searchRequest; - } + List values = thePids.stream() + .map(Object::toString) + .map(v -> FieldValue.of(v)) + .collect(Collectors.toList()); - private IndexRequest createIndexRequest( - String theIndexName, String theDocumentId, String theObservationDocument, String theDocumentType) { - IndexRequest request = new IndexRequest(theIndexName); - request.id(theDocumentId); - request.source(theObservationDocument, XContentType.JSON); - return request; - } - - @Override - public void deleteObservationDocument(String theDocumentId) { - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(OBSERVATION_INDEX); - deleteByQueryRequest.setQuery(QueryBuilders.termQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, theDocumentId)); - try { - myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1191) + "Unable to delete Observation " + theDocumentId); - } - } - - @VisibleForTesting - public void deleteAllDocumentsForTest(String theIndexName) throws IOException { - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(theIndexName); - deleteByQueryRequest.setQuery(QueryBuilders.matchAllQuery()); - myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT); + return SearchRequest.of(sr -> sr.index(OBSERVATION_INDEX) + .query(qb -> qb.bool(bb -> bb.must(bbm -> { + bbm.terms(terms -> + terms.field(OBSERVATION_IDENTIFIER_FIELD_NAME).terms(termsb -> termsb.value(values))); + return bbm; + }))) + .size(thePids.size())); } @VisibleForTesting public void refreshIndex(String theIndexName) throws IOException { - myRestHighLevelClient.indices().refresh(new RefreshRequest(theIndexName), RequestOptions.DEFAULT); + myRestHighLevelClient.indices().refresh(fn -> fn.index(theIndexName)); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java index c6f8209fe0f..1ddf74ac6c0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java @@ -19,10 +19,6 @@ */ package ca.uhn.fhir.jpa.search.lastn; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -32,59 +28,6 @@ import java.util.List; public interface IElasticsearchSvc { - /** - * Returns identifiers for the last most recent N observations that meet the specified criteria. - * - * @param theSearchParameterMap SearchParameterMap containing search parameters used for filtering the last N observations. Supported parameters include Subject, Patient, Code, Category and Max (the parameter used to determine N). - * @param theFhirContext Current FhirContext. - * @param theMaxResultsToFetch The maximum number of results to return for the purpose of paging. - * @return - */ - List executeLastN( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch); - - /** - * Returns index document for a single Observation - * - * @param theDocumentID Identifier of Observation resource. - * @return - */ - ObservationJson getObservationDocument(String theDocumentID); - - /** - * Returns index document for a single Observation Code that either has a coding that matches a specified Code value and system or that has a specified text value. - * - * @param theCodeSystemHash A hash string constructed from a Code value and Code system used to match to an Observation Code. - * @param theText A text value used to match to an Observation Code. - * @return - */ - CodeJson getObservationCodeDocument(String theCodeSystemHash, String theText); - - /** - * Creates or updates index for an Observation Resource. - * - * @param theDocumentId Identifier for Observation resource. - * @param theObservationDocument Indexing document for Observation. - * @return True if Observation indexed successfully. - */ - Boolean createOrUpdateObservationIndex(String theDocumentId, ObservationJson theObservationDocument); - - /** - * Creates or updates index for an Observation Code. - * - * @param theCodeableConceptID Identifier for Observation resource. - * @param theObservationCodeDocument Indexing document for Observation. - * @return True if Observation Code indexed successfully. - */ - Boolean createOrUpdateObservationCodeIndex(String theCodeableConceptID, CodeJson theObservationCodeDocument); - - /** - * Deletes index for an Observation Resource. - * - * @param theDocumentId Identifier for Observation resource. - */ - void deleteObservationDocument(String theDocumentId); - /** * Invoked when shutting down. */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java index 064e193bfad..ad429000749 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java @@ -39,6 +39,11 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.time.DateUtils; @@ -70,11 +75,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java index 1cbc2a5e69f..0e2305703b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; import org.slf4j.Logger; @@ -47,7 +48,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import javax.annotation.PostConstruct; @Component public class CacheWarmingSvcImpl implements ICacheWarmingSvc, IHasScheduledJobs { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index fe35f7c8d4d..e8f2c66b6c3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -53,6 +53,9 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; @@ -82,9 +85,6 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.jpa.api.dao.IDao.RESOURCE_PID_KEY; import static org.apache.commons.lang3.StringUtils.isBlank; 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 de3a7809bb1..63c6d2bc328 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 @@ -37,6 +37,15 @@ import ca.uhn.fhir.jpa.term.api.ITermConceptClientMappingSvc; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.StringUtils; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; @@ -54,15 +63,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 9372b9b03b9..0869b8e47f0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -91,6 +91,11 @@ import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.collect.ArrayListMultimap; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NonUniqueResultException; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.ObjectUtils; @@ -171,11 +176,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.NonUniqueResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.jpa.entity.TermConceptPropertyBinder.CONCEPT_PROPERTY_PREFIX_NAME; import static ca.uhn.fhir.jpa.term.api.ITermLoaderSvc.LOINC_URI; @@ -665,7 +665,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } private boolean isOracleDialect() { - return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.Oracle12cDialect; + return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.OracleDialect; } private void expandConcepts( diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java index fec6c6290c1..18771e6ef27 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java @@ -19,8 +19,8 @@ */ package ca.uhn.fhir.jpa.util; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; /** * Utility class that provides a proxied entityManager. It can be directly injected or diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java index 545935497c8..2da96d1c54f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java @@ -37,6 +37,9 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.InCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Predicate; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.bidimap.DualHashBidiMap; import org.apache.commons.collections4.bidimap.UnmodifiableBidiMap; @@ -53,9 +56,6 @@ import java.util.Objects; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Predicate; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java index b201778ebc8..a79a7834e9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java @@ -39,7 +39,7 @@ public class ScrollableResultsIterator extends BaseIterator if (myNext == null) { if (myScroll.next()) { hasNext = true; - myNext = (T) myScroll.get(0); + myNext = (T) myScroll.get(); } else { hasNext = false; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java index 8f88c3a88ec..5c8274fcda4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java @@ -24,6 +24,8 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport; @@ -32,9 +34,6 @@ import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - public class JpaValidationSupportChain extends ValidationSupportChain { private final FhirContext myFhirContext; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java index 672a023b65e..848f407b512 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java @@ -12,7 +12,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.math.BigDecimal; import java.util.List; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 6fcd53ab9ab..672c17c1096 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -71,38 +71,6 @@ org.apache.derby derby - - org.eclipse.jetty - jetty-servlets - - - org.eclipse.jetty - jetty-servlet - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-server - org.springframework.boot spring-boot-test @@ -161,12 +129,12 @@ mockito-core - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.testcontainers diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java index 7d93aa506e4..d882b4ed076 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; import ca.uhn.fhir.jpa.dao.r4.ElasticsearchPrefixTest; import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; @@ -11,14 +12,12 @@ import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; import ca.uhn.fhir.jpa.test.config.BlockLargeNumbersOfParamsListener; import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.indices.PutTemplateResponse; +import co.elastic.clients.json.JsonData; import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.indices.PutIndexTemplateRequest; -import org.elasticsearch.common.settings.Settings; import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchIndexSettings; @@ -36,7 +35,7 @@ import org.testcontainers.elasticsearch.ElasticsearchContainer; import javax.sql.DataSource; import java.io.IOException; -import java.util.Arrays; +import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; @@ -65,11 +64,9 @@ public class ElasticsearchWithPrefixConfig { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); - retVal.setJpaDialect(new HapiFhirHibernateJpaDialect(fhirContext().getLocalizer())); - retVal.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); - retVal.setPersistenceProvider(new HibernatePersistenceProvider()); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); + retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -126,14 +123,15 @@ public class ElasticsearchWithPrefixConfig { //This tells elasticsearch to use our custom index naming strategy. extraProperties.put(BackendSettings.backendKey(ElasticsearchBackendSettings.LAYOUT_STRATEGY), IndexNamePrefixLayoutStrategy.class.getName()); - PutIndexTemplateRequest ngramTemplate = new PutIndexTemplateRequest("ngram-template") - .patterns(Arrays.asList("*resourcetable-*", "*termconcept-*")) - .settings(Settings.builder().put("index.max_ngram_diff", 50)); - try { - RestHighLevelClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient("http", host + ":" + httpPort, "", ""); - AcknowledgedResponse acknowledgedResponse = elasticsearchHighLevelRestClient.indices().putTemplate(ngramTemplate, RequestOptions.DEFAULT); - assert acknowledgedResponse.isAcknowledged(); + ElasticsearchClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient("http", host + ":" + httpPort, "", ""); + PutTemplateResponse acknowledgedResponse = elasticsearchHighLevelRestClient + .indices() + .putTemplate(b -> b + .name("ngram-template") + .indexPatterns("*resourcetable-*", "*termconcept-*") + .settings(Map.of("index.max_ngram_diff", JsonData.of(50)))); + assert acknowledgedResponse.acknowledged(); } catch (IOException theE) { theE.printStackTrace(); throw new ConfigurationException("Couldn't connect to the elasticsearch server to create necessary templates. Ensure the Elasticsearch user has permissions to create templates."); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java index 2f85e652a52..f9bc8977988 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java @@ -3,11 +3,10 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.config.ElasticsearchWithPrefixConfig; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.apache.http.util.EntityUtils; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestHighLevelClient; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.cat.IndicesResponse; +import co.elastic.clients.elasticsearch.cat.indices.IndicesRecord; +import co.elastic.clients.elasticsearch.indices.GetIndexResponse; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -17,6 +16,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.elasticsearch.ElasticsearchContainer; import java.io.IOException; +import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -36,13 +36,15 @@ public class ElasticsearchPrefixTest { @Test public void test() throws IOException { //Given - RestHighLevelClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( + ElasticsearchClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), "", ""); //When - RestClient lowLevelClient = elasticsearchHighLevelRestClient.getLowLevelClient(); - Response get = lowLevelClient.performRequest(new Request("GET", "/_cat/indices")); - String catIndexes = EntityUtils.toString(get.getEntity()); + IndicesResponse indicesResponse = elasticsearchHighLevelRestClient + .cat() + .indices(); + + String catIndexes = indicesResponse.valueBody().stream().map(IndicesRecord::index).collect(Collectors.joining(",")); //Then assertThat(catIndexes, containsString(ELASTIC_PREFIX + "-resourcetable-000001")); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java index 22274d942d3..355925c596c 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java @@ -38,6 +38,17 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { @Autowired private ISearchDao mySearchDao; + @BeforeEach + public void enableAdvancedHSearchIndexing() { + myStorageSettings.setLastNEnabled(true); + myStorageSettings.setAdvancedHSearchIndexing(true); + } + + @AfterEach + public void disableAdvancedHSearchIndex() { + myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); + } + @Override @BeforeEach public void before() throws Exception { @@ -123,7 +134,7 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { // The first chunked query should have a full complement of PIDs StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'"); for (int pidIndex = 1; pidIndex < 50; pidIndex++) { - firstQueryPattern.append(" , '[0-9]+'"); + firstQueryPattern.append(",'[0-9]+'"); } firstQueryPattern.append("\\).*"); assertThat(queries.get(4), matchesPattern(firstQueryPattern.toString())); @@ -131,10 +142,10 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { // the second chunked query should be padded with "-1". StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'"); for (int pidIndex = 1; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(" , '[0-9]+'"); + secondQueryPattern.append(",'[0-9]+'"); } for (int pidIndex = 0; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(" , '-1'"); + secondQueryPattern.append(",'-1'"); } secondQueryPattern.append("\\).*"); assertThat(queries.get(5), matchesPattern(secondQueryPattern.toString())); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java index f9307f1cfb2..33dbef51e44 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java @@ -1,10 +1,11 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.dao.IHSearchEventListener; import ca.uhn.fhir.jpa.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.test.util.TestHSearchEventDispatcher; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; import org.hl7.fhir.instance.model.api.IIdType; @@ -14,7 +15,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.IOException; @@ -25,26 +30,37 @@ import java.util.UUID; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.matchesPattern; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchLastNIT.class); + @BeforeEach public void enableAdvancedHSearchIndexing() { myStorageSettings.setLastNEnabled(true); + myStorageSettings.setAdvancedHSearchIndexing(true); + myHSearchEventDispatcher.register(mySearchEventListener); + ourLog.info("enableAdvancedHSearchIndexing finished. lastn {} advancedHSearchIndexing {}", myStorageSettings.isLastNEnabled(), myStorageSettings.isAdvancedHSearchIndexing()); } @AfterEach public void reset() { SearchBuilder.setMaxPageSize50ForTest(false); myStorageSettings.setStoreResourceInHSearchIndex(new JpaStorageSettings().isStoreResourceInHSearchIndex()); + myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); } + @Autowired + private TestHSearchEventDispatcher myHSearchEventDispatcher; + + @Mock + private IHSearchEventListener mySearchEventListener; + + + @Test public void testLastNChunking() { @@ -76,28 +92,22 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { .getSelectQueriesForCurrentThread() .stream() .map(t -> t.getSql(true, false)) - .collect(Collectors.toList()); + .toList(); // Two chunked queries executed by the QueryIterator (in current thread) and two chunked queries to retrieve resources by PID. assertEquals(4, queries.size()); // The first and third chunked queries should have a full complement of PIDs StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'"); - for (int pidIndex = 1; pidIndex < 50; pidIndex++) { - firstQueryPattern.append(",'[0-9]+'"); - } + firstQueryPattern.append(",'[0-9]+'".repeat(49)); firstQueryPattern.append("\\).*"); assertThat(queries.get(0).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString())); assertThat(queries.get(2).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString())); // the second and fourth chunked queries should be padded with "-1". StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'"); - for (int pidIndex = 1; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(",'[0-9]+'"); - } - for (int pidIndex = 0; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(",'-1'"); - } + secondQueryPattern.append(",'[0-9]+'".repeat(24)); + secondQueryPattern.append(",'-1'".repeat(25)); secondQueryPattern.append("\\).*"); assertThat(queries.get(1).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString())); assertThat(queries.get(3).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString())); @@ -134,18 +144,12 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { } + /** + * We pull the resources from Hibernate Search when LastN uses Hibernate Search + * Override the test verification to validate only one search was performed + */ void verifyResourcesLoadedFromElastic(List theObservationIds, List theResults) { - List expectedArgumentPids = JpaPid.fromLongList( - theObservationIds.stream().map(IIdType::getIdPartAsLong).collect(Collectors.toList()) - ); - ArgumentCaptor> actualPids = ArgumentCaptor.forClass(List.class); - verify(myElasticsearchSvc, times(1)).getObservationResources(actualPids.capture()); - assertThat(actualPids.getValue(), is(expectedArgumentPids)); - - List expectedObservationList = theObservationIds.stream() - .map(id -> id.toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); - assertEquals(expectedObservationList, theResults); - + Mockito.verify(mySearchEventListener, Mockito.times(1)) + .hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); } - } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java deleted file mode 100644 index 0e547dca1f7..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -/** - * Run entire @see {@link FhirResourceDaoR4SearchLastNAsyncIT} test suite this time - * using Extended HSearch index as search target - */ -@ExtendWith(SpringExtension.class) -public class FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT extends FhirResourceDaoR4SearchLastNAsyncIT { - - @BeforeEach - public void enableAdvancedHSearchIndexing() { - myStorageSettings.setLastNEnabled(true); - myStorageSettings.setAdvancedHSearchIndexing(true); - } - - @AfterEach - public void disableAdvancedHSearchIndex() { - myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java deleted file mode 100644 index 057ed57e48e..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java +++ /dev/null @@ -1,60 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.dao.IHSearchEventListener; -import ca.uhn.fhir.jpa.test.util.TestHSearchEventDispatcher; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.List; - -/** - * Run entire @see {@link FhirResourceDaoR4SearchLastNIT} test suite this time - * using Extended HSearch index as search target. - * - * The other implementation is obsolete, and we can merge these someday. - */ -@ExtendWith(SpringExtension.class) -public class FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT extends FhirResourceDaoR4SearchLastNIT { - private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.class); - - @Autowired - private TestHSearchEventDispatcher myHSearchEventDispatcher; - - @Mock - private IHSearchEventListener mySearchEventListener; - - - @BeforeEach - public void enableAdvancedHSearchIndexing() { - myStorageSettings.setLastNEnabled(true); - myStorageSettings.setAdvancedHSearchIndexing(true); - myHSearchEventDispatcher.register(mySearchEventListener); - ourLog.info("enableAdvancedHSearchIndexing finished. lastn {} advancedHSearchIndexing {}", myStorageSettings.isLastNEnabled(), myStorageSettings.isAdvancedHSearchIndexing()); - - } - - @AfterEach - public void disableAdvancedHSearchIndex() { - myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); - } - - /** - * We pull the resources from Hibernate Search when LastN uses Hibernate Search - * Override the test verification to validate only one search was performed - */ - @Override - void verifyResourcesLoadedFromElastic(List theObservationIds, List theResults) { - Mockito.verify(mySearchEventListener, Mockito.times(1)) - .hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java index 16f6a63cc19..b24e2b46ab8 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java @@ -104,7 +104,7 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java index 452217fe751..77d3a743f65 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java @@ -42,7 +42,7 @@ import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java deleted file mode 100644 index 8d9d0d8c15e..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java +++ /dev/null @@ -1,474 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticHSearch; -import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import com.google.common.base.Charsets; -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.Observation; -import org.hl7.fhir.r4.model.Reference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -@ExtendWith(SpringExtension.class) -@RequiresDocker -@ContextConfiguration(classes = TestR4ConfigWithElasticHSearch.class) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class PersistObservationIndexedSearchParamLastNR4IT { - - private final String SINGLE_SUBJECT_ID = "4567"; - private final String SINGLE_OBSERVATION_PID = "123"; - private final Date SINGLE_EFFECTIVEDTM = new Date(); - private final String SINGLE_OBSERVATION_CODE_TEXT = "Test Codeable Concept Field for Code"; - private final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category"; - private final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate"; - private final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code"; - private final String CODEFIRSTCODINGCODE = "test-code"; - @PersistenceContext(type = PersistenceContextType.TRANSACTION) - protected EntityManager myEntityManager; - @Autowired - protected FhirContext myFhirCtx; - @Autowired - ObservationLastNIndexPersistSvc testObservationPersist; - @Autowired - private ElasticsearchSvcImpl elasticsearchSvc; - @Autowired - private IFhirSystemDao myDao; - @Autowired - private JpaStorageSettings myStorageSettings; - private ReferenceAndListParam multiSubjectParams = null; - - @BeforeEach - public void before() throws IOException { - myStorageSettings.setLastNEnabled(true); - - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @AfterEach - public void afterDisableLastN() { - myStorageSettings.setLastNEnabled(new JpaStorageSettings().isLastNEnabled()); - } - - @Order(0) - @Test - public void testDeleteObservation() throws IOException { - indexMultipleObservations(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - List observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(100, observationDocuments.size()); - // Check that fifth observation for fifth patient has been indexed. - ObservationJson observation = elasticsearchSvc.getObservationDocument("55"); - assertNotNull(observation); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams); - searchParameterMap.setLastNMax(10); - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(100, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains("55")); - - // Delete fifth observation for fifth patient. - ResourceTable entity = new ResourceTable(); - entity.setId(55L); - entity.setResourceType("Observation"); - entity.setVersionForUnitTest(0L); - - testObservationPersist.deleteObservationIndex(entity); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - - // Confirm that observation was deleted. - searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(99, observationDocuments.size()); - observation = elasticsearchSvc.getObservationDocument("55"); - assertNull(observation); - - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(99, observationIdsOnly.size()); - assertTrue(!observationIdsOnly.contains("55")); - - } - - - - private void indexSingleObservation() throws IOException { - - Observation myObservation = new Observation(); - IdType observationID = new IdType("Observation", SINGLE_OBSERVATION_PID, "1"); - myObservation.setId(observationID); - Reference subjectId = new Reference(SINGLE_SUBJECT_ID); - myObservation.setSubject(subjectId); - myObservation.setEffective(new DateTimeType(SINGLE_EFFECTIVEDTM)); - - myObservation.setCategory(getCategoryCode()); - - myObservation.setCode(getObservationCode()); - - testObservationPersist.indexObservation(myObservation); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - private List getCategoryCode() { - // Add three CodeableConcepts for category - List categoryConcepts = new ArrayList<>(); - // Create three codings and first category CodeableConcept - List category = new ArrayList<>(); - CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category"); - category.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, "test-heart-rate display")); - category.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display")); - category.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display")); - categoryCodeableConcept1.setCoding(category); - categoryConcepts.add(categoryCodeableConcept1); - // Create three codings and second category CodeableConcept - List category2 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for for second category"); - category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vital-signs", "test-vital-signs display")); - category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display")); - category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display")); - categoryCodeableConcept2.setCoding(category2); - categoryConcepts.add(categoryCodeableConcept2); - // Create three codings and third category CodeableConcept - List category3 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText("Test Codeable Concept Field for third category"); - category3.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vitals-panel", "test-vitals-panel display")); - category3.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test-alt-vitals-panel display")); - category3.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals-panel", "test-2nd-alt-vitals-panel display")); - categoryCodeableConcept3.setCoding(category3); - categoryConcepts.add(categoryCodeableConcept3); - return categoryConcepts; - } - - private CodeableConcept getObservationCode() { - // Create CodeableConcept for Code with three codings. - CodeableConcept codeableConceptField = new CodeableConcept().setText(SINGLE_OBSERVATION_CODE_TEXT); - codeableConceptField.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, "test-code display")); - return codeableConceptField; - } - - @Order(1) - @Test - public void testSampleBundleInTransaction() throws IOException { - FhirContext myFhirCtx = FhirContext.forR4Cached(); - - PathMatchingResourcePatternResolver provider = new PathMatchingResourcePatternResolver(); - final Resource[] bundleResources = provider.getResources("lastntestbundle.json"); - assertEquals(1, bundleResources.length); - - AtomicInteger index = new AtomicInteger(); - - Arrays.stream(bundleResources).forEach( - resource -> { - index.incrementAndGet(); - - InputStream resIs = null; - String nextBundleString; - try { - resIs = resource.getInputStream(); - nextBundleString = IOUtils.toString(resIs, Charsets.UTF_8); - } catch (IOException e) { - return; - } finally { - try { - if (resIs != null) { - resIs.close(); - } - } catch (final IOException ioe) { - // ignore - } - } - - IParser parser = myFhirCtx.newJsonParser(); - Bundle bundle = parser.parseResource(Bundle.class, nextBundleString); - - myDao.transaction(null, bundle); - - } - ); - - elasticsearchSvc.refreshIndex("*"); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - - // execute Observation ID search - Composite Aggregation - searchParameterMap.setLastNMax(1); - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - assertEquals(20, observationIdsOnly.size()); - - searchParameterMap.setLastNMax(3); - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - assertEquals(38, observationIdsOnly.size()); - - } - - @Order(2) - @Test - public void testIndexObservationMultiple() throws IOException { - indexMultipleObservations(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - List observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(100, observationDocuments.size()); - - // Check that all observations were indexed. - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams); - - searchParameterMap.setLastNMax(10); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(100, observationIdsOnly.size()); - - // Filter the results by category code. - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - - - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100); - - assertEquals(50, observationIdsOnly.size()); - - } - - private void indexMultipleObservations() throws IOException { - - // Create two CodeableConcept values each for a Code with three codings. - CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code"); - codeableConceptField1.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, "test-code-1", "test-code-1 display")); - codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display")); - codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display")); - - CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code"); - codeableConceptField2.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, "test-code-2", "test-code-2 display")); - codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display")); - codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display")); - - // Create two CodeableConcept entities for category, each with three codings. - List category1 = new ArrayList<>(); - // Create three codings and first category CodeableConcept - category1.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, "test-heart-rate display")); - category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display")); - category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display")); - List categoryConcepts1 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category"); - categoryCodeableConcept1.setCoding(category1); - categoryConcepts1.add(categoryCodeableConcept1); - // Create three codings and second category CodeableConcept - List category2 = new ArrayList<>(); - category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vital-signs", "test-vital-signs display")); - category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display")); - category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display")); - List categoryConcepts2 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category"); - categoryCodeableConcept2.setCoding(category2); - categoryConcepts2.add(categoryCodeableConcept2); - - ReferenceOrListParam subjectParams = new ReferenceOrListParam(); - for (int patientCount = 0; patientCount < 10; patientCount++) { - - String subjectId = String.valueOf(patientCount); - - ReferenceParam subjectParam = new ReferenceParam("Patient", "", subjectId); - subjectParams.addOr(subjectParam); - - for (int entryCount = 0; entryCount < 10; entryCount++) { - - Observation observation = new Observation(); - IdType observationId = new IdType("Observation", String.valueOf(entryCount + patientCount * 10), "1"); - observation.setId(observationId); - Reference subject = new Reference(subjectId); - observation.setSubject(subject); - - if (entryCount % 2 == 1) { - observation.setCategory(categoryConcepts1); - observation.setCode(codeableConceptField1); - } else { - observation.setCategory(categoryConcepts2); - observation.setCode(codeableConceptField2); - } - - Calendar observationDate = new GregorianCalendar(); - observationDate.add(Calendar.HOUR, -10 + entryCount); - Date effectiveDtm = observationDate.getTime(); - observation.setEffective(new DateTimeType(effectiveDtm)); - - testObservationPersist.indexObservation(observation); - } - - } - - multiSubjectParams = new ReferenceAndListParam().addAnd(subjectParams); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @Order(3) - @Test - public void testIndexObservationSingle() throws IOException { - indexSingleObservation(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(10); - List persistedObservationEntities = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(1, persistedObservationEntities.size()); - ObservationJson persistedObservationEntity = persistedObservationEntities.get(0); - assertEquals(SINGLE_SUBJECT_ID, persistedObservationEntity.getSubject()); - assertEquals(SINGLE_OBSERVATION_PID, persistedObservationEntity.getIdentifier()); - assertEquals(SINGLE_EFFECTIVEDTM, persistedObservationEntity.getEffectiveDtm()); - - String observationCodeNormalizedId = persistedObservationEntity.getCode_concept_id(); - - // List persistedObservationCodes = elasticsearchSvc.queryAllIndexedObservationCodesForTest(); - // assertEquals(1, persistedObservationCodes.size()); - - // Check that we can retrieve code by hash value. - String codeSystemHash = persistedObservationEntity.getCode_coding_code_system_hash(); - CodeJson persistedObservationCode = elasticsearchSvc.getObservationCodeDocument(codeSystemHash, null); - assertNotNull(persistedObservationCode); - assertEquals(observationCodeNormalizedId, persistedObservationCode.getCodeableConceptId()); - assertEquals(SINGLE_OBSERVATION_CODE_TEXT, persistedObservationCode.getCodeableConceptText()); - - // Also confirm that we can retrieve code by text value. - persistedObservationCode = elasticsearchSvc.getObservationCodeDocument(null, SINGLE_OBSERVATION_CODE_TEXT); - assertNotNull(persistedObservationCode); - - searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(3); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100); - - assertEquals(1, observationIdsOnly.size()); - assertEquals(SINGLE_OBSERVATION_PID, observationIdsOnly.get(0)); - } - - @Order(4) - @Test - public void testUpdateObservation() throws IOException { - indexSingleObservation(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(10); - ObservationJson observationIndexEntity = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx).get(0); - assertEquals(SINGLE_OBSERVATION_PID, observationIndexEntity.getIdentifier()); - assertEquals(SINGLE_SUBJECT_ID, observationIndexEntity.getSubject()); - assertEquals(SINGLE_EFFECTIVEDTM, observationIndexEntity.getEffectiveDtm()); - - searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(10); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(1, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains(SINGLE_OBSERVATION_PID)); - - // Update the Observation with a new Subject and effective date: - Observation updatedObservation = new Observation(); - IdType observationId = new IdType("Observation", observationIndexEntity.getIdentifier(), "2"); - updatedObservation.setId(observationId); - Reference subjectId = new Reference("1234"); - updatedObservation.setSubject(subjectId); - DateTimeType newEffectiveDtm = new DateTimeType(new Date()); - updatedObservation.setEffective(newEffectiveDtm); - updatedObservation.setCategory(getCategoryCode()); - updatedObservation.setCode(getObservationCode()); - - testObservationPersist.indexObservation(updatedObservation); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - - ObservationJson updatedObservationEntity = elasticsearchSvc.getObservationDocument(SINGLE_OBSERVATION_PID); - assertEquals("1234", updatedObservationEntity.getSubject()); - assertEquals(newEffectiveDtm.getValue(), updatedObservationEntity.getEffectiveDtm()); - - // Repeat earlier Elasticsearch query. This time, should return no matches. - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(0, observationIdsOnly.size()); - - // Try again with the new patient ID. - searchParameterMap = new SearchParameterMap(); - subjectParam = new ReferenceParam("Patient", "", "1234"); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(10); - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - // Should see the observation returned now. - assertEquals(1, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains(SINGLE_OBSERVATION_PID)); - - } - - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java index bb9573636b7..6e25869547a 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java @@ -38,7 +38,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.Objects; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java deleted file mode 100644 index bd32380e149..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java +++ /dev/null @@ -1,511 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.DateOrListParam; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.param.TokenParamModifier; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.hl7.fhir.r4.model.Observation; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.elasticsearch.ElasticsearchContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.TEST_BASELINE_TIMESTAMP; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExtendWith({SpringExtension.class}) -@RequiresDocker -@Testcontainers -public class LastNElasticsearchSvcMultipleObservationsIT { - - @Container - public static ElasticsearchContainer elasticsearchContainer = TestElasticsearchContainerHelper.getEmbeddedElasticSearch(); - private static boolean indexLoaded = false; - private final Map>> createdPatientObservationMap = new HashMap<>(); - private final FhirContext myFhirContext = FhirContext.forR4Cached(); - private ElasticsearchSvcImpl elasticsearchSvc; - - @BeforeEach - public void before() throws IOException { - PartitionSettings partitionSettings = new PartitionSettings(); - partitionSettings.setPartitioningEnabled(false); - elasticsearchSvc = new ElasticsearchSvcImpl(partitionSettings, "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), null, null); - - if (!indexLoaded) { - createMultiplePatientsAndObservations(); - indexLoaded = true; - } - } - - @AfterEach - public void after() throws IOException { - elasticsearchSvc.close(); - } - - @Test - public void testLastNAllPatientsQuery() { - - // execute Observation ID search (Composite Aggregation) last 3 observations for each patient - SearchParameterMap searchParameterMap = new SearchParameterMap(); - IntStream.range(0, 10).forEach(index -> { - ReferenceParam subjectParam = new ReferenceParam("Patient", "", String.valueOf(index)); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - }); - searchParameterMap.setLastNMax(3); - - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - assertEquals(60, observations.size()); - - // Observation documents should be grouped by subject, then by observation code, and then sorted by effective date/time - // within each observation code. Verify the grouping by creating a nested Map. - Map>> queriedPatientObservationMap = new HashMap<>(); - ObservationJson previousObservationJson = null; - for (ObservationJson observationJson : observations) { - assertNotNull(observationJson.getIdentifier()); - assertNotNull(observationJson.getSubject()); - assertNotNull(observationJson.getCode_concept_id()); - assertNotNull(observationJson.getEffectiveDtm()); - if (previousObservationJson == null) { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observationJson.getCode_concept_id(), observationDates); - queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap); - } else if (observationJson.getSubject().equals(previousObservationJson.getSubject())) { - if (observationJson.getCode_concept_id().equals(previousObservationJson.getCode_concept_id())) { - queriedPatientObservationMap.get(observationJson.getSubject()).get(observationJson.getCode_concept_id()). - add(observationJson.getEffectiveDtm()); - } else { - Map> codeObservationDateMap = queriedPatientObservationMap.get(observationJson.getSubject()); - // Ensure that code concept was not already retrieved out of order for this subject/patient. - assertFalse(codeObservationDateMap.containsKey(observationJson.getCode_concept_id())); - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - codeObservationDateMap.put(observationJson.getCode_concept_id(), observationDates); - } - } else { - // Ensure that subject/patient was not already retrieved out of order - assertFalse(queriedPatientObservationMap.containsKey(observationJson.getSubject())); - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observationJson.getCode_concept_id(), observationDates); - queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap); - } - previousObservationJson = observationJson; - } - - // Finally check that only the most recent effective date/time values were returned and in the correct order. - for (String subjectId : queriedPatientObservationMap.keySet()) { - Map> queriedObservationCodeMap = queriedPatientObservationMap.get(subjectId); - Map> createdObservationCodeMap = createdPatientObservationMap.get(subjectId); - for (String observationCode : queriedObservationCodeMap.keySet()) { - List queriedObservationDates = queriedObservationCodeMap.get(observationCode); - List createdObservationDates = createdObservationCodeMap.get(observationCode); - for (int dateIdx = 0; dateIdx < queriedObservationDates.size(); dateIdx++) { - assertEquals(createdObservationDates.get(dateIdx), queriedObservationDates.get(dateIdx)); - } - } - } - - } - - @Test - public void testLastNMultiPatientMultiCodeHashMultiCategoryHash() { - // Multiple Subject references - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", "3"); - ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", "5"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2)); - TokenParam categoryParam1 = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - TokenParam categoryParam2 = new TokenParam("http://mycodes.org/fhir/observation-category", "test-vital-signs"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2)); - TokenParam codeParam1 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - TokenParam codeParam2 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-2"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(20, observations.size()); - - // Repeat with multiple Patient parameter - searchParameterMap = new SearchParameterMap(); - ReferenceParam patientParam1 = new ReferenceParam("Patient", "", "8"); - ReferenceParam patientParam2 = new ReferenceParam("Patient", "", "6"); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam1, patientParam2)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(20, observations.size()); - - } - - private ReferenceAndListParam buildReferenceAndListParam(ReferenceParam... theReference) { - ReferenceOrListParam myReferenceOrListParam = new ReferenceOrListParam(); - for (ReferenceParam referenceParam : theReference) { - myReferenceOrListParam.addOr(referenceParam); - } - return new ReferenceAndListParam().addAnd(myReferenceOrListParam); - } - - private TokenAndListParam buildTokenAndListParam(TokenParam... theToken) { - TokenOrListParam myTokenOrListParam = new TokenOrListParam(); - for (TokenParam tokenParam : theToken) { - myTokenOrListParam.addOr(tokenParam); - } - return new TokenAndListParam().addAnd(myTokenOrListParam); - } - - @Test - public void testLastNCodeCodeOnlyCategoryCodeOnly() { - // Include subject - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam(null, "test-heart-rate"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam(null, "test-code-1"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - } - - @Test - public void testLastNCodeSystemOnlyCategorySystemOnly() { - // Include subject and patient - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", null); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", null); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(10, observations.size()); - } - - @Test - public void testLastNCodeCodeTextCategoryTextOnly() { - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - - // Check case match - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam("Heart"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam("Code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check case not match - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("heart"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check hyphenated strings - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("heart-rate"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check partial strings - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("hear"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("1-obs"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - } - - @Test - public void testLastNNoMatchQueries() { - - ReferenceParam validPatientParam = new ReferenceParam("Patient", "", "9"); - TokenParam validCategoryCodeParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - TokenParam validObservationCodeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - DateParam validDateParam = new DateParam(ParamPrefixEnum.EQUAL, new Date(TEST_BASELINE_TIMESTAMP - (9 * 3600 * 1000))); - - // Ensure that valid parameters are indeed valid - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(1, observations.size()); - - // Invalid Patient - searchParameterMap = new SearchParameterMap(); - ReferenceParam patientParam = new ReferenceParam("Patient", "", "10"); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid subject - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(patientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid observation code - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-999"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid category code - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-not-a-category"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid date - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.GREATERTHAN, TEST_BASELINE_TIMESTAMP)); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - } - - @Test - public void testLastNEffectiveDates() { - Date highDate = new Date(TEST_BASELINE_TIMESTAMP - (3600 * 1000)); - Date lowDate = new Date(TEST_BASELINE_TIMESTAMP - (10 * 3600 * 1000)); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - DateParam dateParam = new DateParam(ParamPrefixEnum.EQUAL, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(1, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(10, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.STARTS_AFTER, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(10, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.ENDS_BEFORE, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - DateParam startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(TEST_BASELINE_TIMESTAMP - (4 * 3600 * 1000))); - DateAndListParam dateAndListParam = new DateAndListParam(); - dateAndListParam.addAnd(new DateOrListParam().addOr(startDateParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); - dateAndListParam.addAnd(new DateOrListParam().addOr(dateParam)); - searchParameterMap.add(Observation.SP_DATE, dateAndListParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(3, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(TEST_BASELINE_TIMESTAMP - (4 * 3600 * 1000))); - searchParameterMap.add(Observation.SP_DATE, startDateParam); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - } - - private void createMultiplePatientsAndObservations() throws IOException { - List patientIds = IntStream.range(0, 10).boxed().collect(Collectors.toList()); - List observations = LastNTestDataGenerator.createMultipleObservationJson(patientIds); - - observations.forEach(observation -> { - CodeJson codeJson = observation.getCode(); - assertTrue(elasticsearchSvc.createOrUpdateObservationCodeIndex(codeJson.getCodeableConceptId(), codeJson)); - assertTrue(elasticsearchSvc.createOrUpdateObservationIndex(observation.getIdentifier(), observation)); - - String subject = observation.getSubject(); - if (createdPatientObservationMap.containsKey(subject)) { - Map> observationCodeMap = createdPatientObservationMap.get(subject); - if (observationCodeMap.containsKey(observation.getCode_concept_id())) { - List observationDates = observationCodeMap.get(observation.getCode_concept_id()); - observationDates.add(observation.getEffectiveDtm()); - observationDates.sort(Collections.reverseOrder()); - } else { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observation.getEffectiveDtm()); - observationCodeMap.put(observation.getCode_concept_id(), observationDates); - } - } else { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observation.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observation.getCode_concept_id(), observationDates); - createdPatientObservationMap.put(subject, codeObservationMap); - } - }); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @Test - public void testLastNNoParamsQuery() { - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(1); - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - assertEquals(2, observations.size()); - - String observationCode1 = observations.get(0).getCode_coding_code_system_hash(); - String observationCode2 = observations.get(1).getCode_coding_code_system_hash(); - - assertNotEquals(observationCode1, observationCode2); - - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java deleted file mode 100644 index 962241e227b..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java +++ /dev/null @@ -1,262 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper; -import ca.uhn.fhir.model.dstu2.resource.Observation; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.elasticsearch.ElasticsearchContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.io.IOException; -import java.util.Date; -import java.util.List; - -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYFIRSTCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYSECONDCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYTHIRDCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTHIRDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.OBSERVATIONSINGLECODEID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.OBSERVATION_CODE_CONCEPT_TEXT_1; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTHIRDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SINGLE_OBSERVATION_RESOURCE_PID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SINGLE_OBSERVATION_SUBJECT_ID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.TEST_BASELINE_TIMESTAMP; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTHIRDCODINGDISPLAY; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExtendWith(SpringExtension.class) -@RequiresDocker -@Testcontainers -public class LastNElasticsearchSvcSingleObservationIT { - - private final FhirContext myFhirContext = FhirContext.forR4Cached(); - - ElasticsearchSvcImpl elasticsearchSvc; - - @Container - public static ElasticsearchContainer elasticsearchContainer = TestElasticsearchContainerHelper.getEmbeddedElasticSearch(); - - - @BeforeEach - public void before() { - PartitionSettings partitionSettings = new PartitionSettings(); - partitionSettings.setPartitioningEnabled(false); - elasticsearchSvc = new ElasticsearchSvcImpl(partitionSettings, "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), "", ""); - } - - @AfterEach - public void after() throws IOException { - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - } - - @Test - public void testSingleObservationQuery() throws IOException { - - // Create test observation - ObservationJson indexedObservation = LastNTestDataGenerator.createSingleObservationJson(); - assertTrue(elasticsearchSvc.createOrUpdateObservationIndex(SINGLE_OBSERVATION_RESOURCE_PID, indexedObservation)); - assertTrue(elasticsearchSvc.createOrUpdateObservationCodeIndex(OBSERVATIONSINGLECODEID, indexedObservation.getCode())); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_OBSERVATION_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.EQUAL, new Date(TEST_BASELINE_TIMESTAMP))); - - searchParameterMap.setLastNMax(3); - - // execute Observation ID search - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(1, observationIdsOnly.size()); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observationIdsOnly.get(0)); - - // execute Observation search for all search fields - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - validateFullObservationSearch(observations); - } - - private void validateFullObservationSearch(List observations) throws IOException { - - assertEquals(1, observations.size()); - ObservationJson observation = observations.get(0); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observation.getIdentifier()); - - assertEquals(SINGLE_OBSERVATION_SUBJECT_ID, observation.getSubject()); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observation.getIdentifier()); - assertEquals(new Date(TEST_BASELINE_TIMESTAMP), observation.getEffectiveDtm()); - assertEquals(OBSERVATIONSINGLECODEID, observation.getCode_concept_id()); - - List category_concept_text_values = observation.getCategory_concept_text(); - assertEquals(3, category_concept_text_values.size()); - assertEquals(FIRSTCATEGORYTEXT, category_concept_text_values.get(0)); - assertEquals(SECONDCATEGORYTEXT, category_concept_text_values.get(1)); - assertEquals(THIRDCATEGORYTEXT, category_concept_text_values.get(2)); - - List> category_codings_systems = observation.getCategory_coding_system(); - assertEquals(3, category_codings_systems.size()); - List category_coding_systems = category_codings_systems.get(0); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - category_coding_systems = category_codings_systems.get(1); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - category_coding_systems = category_codings_systems.get(2); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - - List> category_codings_codes = observation.getCategory_coding_code(); - assertEquals(3, category_codings_codes.size()); - List category_coding_codes = category_codings_codes.get(0); - assertEquals(3, category_coding_codes.size()); - assertEquals(FIRSTCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(FIRSTCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(FIRSTCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - category_coding_codes = category_codings_codes.get(1); - assertEquals(3, category_coding_codes.size()); - assertEquals(SECONDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(SECONDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(SECONDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - category_coding_codes = category_codings_codes.get(2); - assertEquals(3, category_coding_codes.size()); - assertEquals(THIRDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(THIRDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(THIRDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - - List> category_codings_displays = observation.getCategory_coding_display(); - assertEquals(3, category_codings_displays.size()); - List category_coding_displays = category_codings_displays.get(0); - assertEquals(FIRSTCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(FIRSTCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(FIRSTCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - category_coding_displays = category_codings_displays.get(1); - assertEquals(3, category_coding_displays.size()); - assertEquals(SECONDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(SECONDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(SECONDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - category_coding_displays = category_codings_displays.get(2); - assertEquals(3, category_coding_displays.size()); - assertEquals(THIRDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(THIRDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(THIRDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - - List> category_codings_code_system_hashes = observation.getCategory_coding_code_system_hash(); - assertEquals(3, category_codings_code_system_hashes.size()); - List category_coding_code_system_hashes = category_codings_code_system_hashes.get(0); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - category_coding_code_system_hashes = category_codings_code_system_hashes.get(1); - assertEquals(3, category_coding_code_system_hashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - category_coding_code_system_hashes = category_codings_code_system_hashes.get(2); - assertEquals(3, category_coding_code_system_hashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - - String code_concept_text_values = observation.getCode_concept_text(); - assertEquals(OBSERVATION_CODE_CONCEPT_TEXT_1, code_concept_text_values); - - String code_coding_systems = observation.getCode_coding_system(); - assertEquals(CODEFIRSTCODINGSYSTEM, code_coding_systems); - - String code_coding_codes = observation.getCode_coding_code(); - assertEquals(CODEFIRSTCODINGCODE, code_coding_codes); - - String code_coding_display = observation.getCode_coding_display(); - assertEquals(CODEFIRSTCODINGDISPLAY, code_coding_display); - - String code_coding_code_system_hash = observation.getCode_coding_code_system_hash(); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), code_coding_code_system_hash); - - // Retrieve all Observation codes - List codes = elasticsearchSvc.queryAllIndexedObservationCodesForTest(); - assertEquals(1, codes.size()); - CodeJson persistedObservationCode = codes.get(0); - - String persistedCodeConceptID = persistedObservationCode.getCodeableConceptId(); - assertEquals(OBSERVATIONSINGLECODEID, persistedCodeConceptID); - String persistedCodeConceptText = persistedObservationCode.getCodeableConceptText(); - assertEquals(OBSERVATION_CODE_CONCEPT_TEXT_1, persistedCodeConceptText); - - List persistedCodeCodingSystems = persistedObservationCode.getCoding_system(); - assertEquals(1, persistedCodeCodingSystems.size()); - assertEquals(CODEFIRSTCODINGSYSTEM, persistedCodeCodingSystems.get(0)); - - List persistedCodeCodingCodes = persistedObservationCode.getCoding_code(); - assertEquals(1, persistedCodeCodingCodes.size()); - assertEquals(CODEFIRSTCODINGCODE, persistedCodeCodingCodes.get(0)); - - List persistedCodeCodingDisplays = persistedObservationCode.getCoding_display(); - assertEquals(1, persistedCodeCodingDisplays.size()); - assertEquals(CODEFIRSTCODINGDISPLAY, persistedCodeCodingDisplays.get(0)); - - List persistedCodeCodingCodeSystemHashes = persistedObservationCode.getCoding_code_system_hash(); - assertEquals(1, persistedCodeCodingCodeSystemHashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(0)); - - - } - -} - diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java deleted file mode 100644 index ca7b3172d1b..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java +++ /dev/null @@ -1,144 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class LastNTestDataGenerator { - - public static final long TEST_BASELINE_TIMESTAMP = new Date().toInstant().toEpochMilli(); - - public static final String SINGLE_OBSERVATION_RESOURCE_PID = "123"; - public static final String SINGLE_OBSERVATION_SUBJECT_ID = "Patient/4567"; - - public static final String FIRSTCATEGORYTEXT = "Test Codeable Concept Field for first category"; - public static final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category"; - public static final String CATEGORYSECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-category"; - public static final String CATEGORYTHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-category"; - public static final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate"; - public static final String FIRSTCATEGORYFIRSTCODINGDISPLAY = "Test Heart Rate"; - public static final String FIRSTCATEGORYSECONDCODINGCODE = "test-alt-heart-rate"; - public static final String FIRSTCATEGORYSECONDCODINGDISPLAY = "Test HeartRate"; - public static final String FIRSTCATEGORYTHIRDCODINGCODE = "test-2nd-alt-heart-rate"; - public static final String FIRSTCATEGORYTHIRDCODINGDISPLAY = "Test Heart-Rate"; - public static final String SECONDCATEGORYTEXT = "Test Codeable Concept Field for for second category"; - public static final String SECONDCATEGORYFIRSTCODINGCODE = "test-vital-signs"; - public static final String SECONDCATEGORYFIRSTCODINGDISPLAY = "Test Vital Signs"; - public static final String SECONDCATEGORYSECONDCODINGCODE = "test-alt-vitals"; - public static final String SECONDCATEGORYSECONDCODINGDISPLAY = "Test Vital-Signs"; - public static final String SECONDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals"; - public static final String SECONDCATEGORYTHIRDCODINGDISPLAY = "Test Vitals"; - public static final String THIRDCATEGORYTEXT = "Test Codeable Concept Field for third category"; - public static final String THIRDCATEGORYFIRSTCODINGCODE = "test-vital-panel"; - public static final String THIRDCATEGORYFIRSTCODINGDISPLAY = "test-vitals-panel display"; - public static final String THIRDCATEGORYSECONDCODINGCODE = "test-alt-vitals-panel"; - public static final String THIRDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display"; - public static final String THIRDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals-panel"; - public static final String THIRDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals-panel display"; - public static final String OBSERVATIONSINGLECODEID = UUID.randomUUID().toString(); - public static final String OBSERVATION_CODE_CONCEPT_TEXT_1 = "Test Codeable Concept Field for First Code"; - public static final String OBSERVATION_CODE_CONCEPT_TEXT_2 = "Test Codeable Concept Field for Second Code"; - public static final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code"; - public static final String CODEFIRSTCODINGCODE = "test-code-1"; - public static final String CODEFIRSTCODINGDISPLAY = "1-Observation Code1"; - public static final String CODE_SECOND_CODING_SYSTEM = "http://mycodes.org/fhir/observation-code"; - public static final String CODE_SECOND_CODING_CODE = "test-code-2"; - public static final String CODE_SECOND_CODING_DISPLAY = "2-Observation Code2"; - - public static ObservationJson createSingleObservationJson() { - ObservationJson indexedObservation = new ObservationJson(); - indexedObservation.setIdentifier(SINGLE_OBSERVATION_RESOURCE_PID); - indexedObservation.setSubject(SINGLE_OBSERVATION_SUBJECT_ID); - indexedObservation.setEffectiveDtm(new Date(TEST_BASELINE_TIMESTAMP)); - - indexedObservation.setCategories(createCategoryCodeableConcepts()); - - // Create CodeableConcept for Code - CodeJson codeableConceptField = new CodeJson(); - codeableConceptField.setCodeableConceptId(OBSERVATIONSINGLECODEID); - codeableConceptField.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_1); - codeableConceptField.addCoding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY); - - indexedObservation.setCode(codeableConceptField); - - return indexedObservation; - } - - private static List createCategoryCodeableConcepts() { - CodeJson categoryCodeableConcept1 = new CodeJson(); - categoryCodeableConcept1.setCodeableConceptText(FIRSTCATEGORYTEXT); - categoryCodeableConcept1.addCoding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, FIRSTCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept1.addCoding(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE, FIRSTCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept1.addCoding(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE, FIRSTCATEGORYTHIRDCODINGDISPLAY); - - CodeJson categoryCodeableConcept2 = new CodeJson(); - categoryCodeableConcept2.setCodeableConceptText(SECONDCATEGORYTEXT); - categoryCodeableConcept2.addCoding(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE, SECONDCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept2.addCoding(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE, SECONDCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept2.addCoding(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE, SECONDCATEGORYTHIRDCODINGDISPLAY); - - CodeJson categoryCodeableConcept3 = new CodeJson(); - categoryCodeableConcept3.setCodeableConceptText(THIRDCATEGORYTEXT); - categoryCodeableConcept3.addCoding(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE, THIRDCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept3.addCoding(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE, THIRDCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept3.addCoding(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE, THIRDCATEGORYTHIRDCODINGDISPLAY); - - return Arrays.asList(categoryCodeableConcept1, categoryCodeableConcept2, categoryCodeableConcept3); - } - - public static List createMultipleObservationJson(List thePatientIds) { - - // CodeableConcept 1 - with 3 codings - String codeableConceptId1 = UUID.randomUUID().toString(); - CodeJson codeJson1 = new CodeJson(); - codeJson1.setCodeableConceptId(codeableConceptId1); - codeJson1.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_1); - codeJson1.addCoding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY); - - // CodeableConcept 2 - with 3 codings - String codeableConceptId2 = UUID.randomUUID().toString(); - CodeJson codeJson2 = new CodeJson(); - codeJson2.setCodeableConceptId(codeableConceptId2); - codeJson2.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_2); - codeJson2.addCoding(CODE_SECOND_CODING_SYSTEM, CODE_SECOND_CODING_CODE, CODE_SECOND_CODING_DISPLAY); - - List categoryCodeableConcepts = createCategoryCodeableConcepts(); - // CategoryCodeableConcept 1 - with 3 codings - List categoryConcepts1 = Collections.singletonList(categoryCodeableConcepts.get(0)); - - // CateogryCodeableConcept 2 - with 3 codings - List categoryConcepts2 = Collections.singletonList(categoryCodeableConcepts.get(1)); - - // Pair CodeableConcept 1 + CategoryCodeableConcept 1 for odd numbered observation - // Pair CodeableConcept 2 + CategoryCodeableConcept 2 for even numbered observation - - // For each patient - create 10 observations - return thePatientIds.stream() - .flatMap(patientId -> IntStream.range(0, 10) - .mapToObj(index -> { - ObservationJson observationJson = new ObservationJson(); - String identifier = String.valueOf((index + patientId * 10L)); - observationJson.setIdentifier(identifier); - observationJson.setSubject(String.valueOf(patientId)); - if (index % 2 == 1) { - observationJson.setCategories(categoryConcepts1); - observationJson.setCode(codeJson1); - } else { - observationJson.setCategories(categoryConcepts2); - observationJson.setCode(codeJson2); - } - Date effectiveDtm = new Date(TEST_BASELINE_TIMESTAMP - ((10L - index) * 3600L * 1000L)); - observationJson.setEffectiveDtm(effectiveDtm); - return observationJson; - })) - .collect(Collectors.toList()); - } - -} diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 1d71e2ac1e7..736c8753f00 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -20,8 +20,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java index 5b2c65b31b7..9fee362d699 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.IoUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.lang3.Validate; @@ -56,7 +57,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.jpa.fql.util.HfqlConstants.PROTOCOL_VERSION; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java index adb3d2d8335..805707e3621 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java @@ -30,6 +30,8 @@ import ca.uhn.fhir.util.DatatypeUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.VersionUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.csv.CSVPrinter; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; @@ -37,8 +39,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.io.OutputStreamWriter; import javax.annotation.Nullable; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.jpa.fql.jdbc.HfqlRestClient.CSV_FORMAT; import static ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8_CTSUFFIX; diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 5c28e18021a..2f75ba5a60f 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -21,8 +21,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java index a085d1b6ea3..fa16b55e2fc 100644 --- a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java +++ b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.ips.api; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -36,7 +37,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; /** * This class is the registry for sections for the IPS document. It can be extended diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 2fb8fa175ad..dd2a1fba351 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -61,8 +61,8 @@ test - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api test diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java index 1acf874d65d..23ca40079fd 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java @@ -26,11 +26,10 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - @Service public class MdmQueueConsumerLoader { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java index 6ea10e08c9f..a9ddf8b322a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; @@ -103,6 +104,9 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { @Autowired IInterceptorBroadcaster myInterceptorBroadcaster; + @Autowired + private HapiTransactionService myTxService; + public MdmControllerSvcImpl() {} @Override @@ -194,7 +198,9 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { @Override public List queryLinkHistory( MdmHistorySearchParameters theMdmHistorySearchParameters, RequestDetails theRequestDetails) { - return myMdmLinkQuerySvc.queryLinkHistory(theMdmHistorySearchParameters); + return myTxService + .withRequest(theRequestDetails) + .execute(() -> myMdmLinkQuerySvc.queryLinkHistory(theMdmHistorySearchParameters)); } @Override diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java index 77a186cc549..7953cc7316a 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java @@ -19,6 +19,7 @@ import ca.uhn.fhir.mdm.model.MdmMetrics; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.EntityManagerFactory; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; @@ -30,7 +31,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; -import javax.persistence.EntityManagerFactory; import java.util.List; import static org.junit.jupiter.api.Assertions.fail; @@ -78,6 +78,7 @@ public class MdmMetricSvcJpaIT extends BaseMdmR4Test implements IMdmMetricSvcTes @Autowired private IMdmMetricSvc mySvc; + @Override @BeforeEach public void before() throws Exception { super.before(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java index dd185421e9f..4a4089ef08e 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java @@ -19,7 +19,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.function.Supplier; import static org.awaitility.Awaitility.await; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java index 31125dd941c..5b1150bc1ca 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index f7e8615c22d..953a7574618 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -172,8 +172,12 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { assertLinksMatchResult(MdmMatchResultEnum.MATCH, MdmMatchResultEnum.POSSIBLE_MATCH, MdmMatchResultEnum.POSSIBLE_MATCH); + logAllTokenIndexes(); + // When - myPatientDao.delete(paulPatient.getIdElement()); + myPatientDao.delete(paulPatient.getIdElement(), new SystemRequestDetails()); + + logAllTokenIndexes(); // Then List resources = myPatientDao.search(new SearchParameterMap(), SystemRequestDetails.forAllPartitions()).getAllResources(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java index 5fa7ce7d19d..8b46fe10d87 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java @@ -54,8 +54,10 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test { myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules()); } + @Override @BeforeEach public void before() throws Exception { + super.before(); myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmHelper, diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java index e9f69df9592..76d6b6d065f 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState; @@ -14,6 +15,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.interceptor.MdmStorageInterceptor; import ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent; @@ -119,10 +121,18 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { @SpyBean private IMdmSubmitSvc myMdmSubmitSvc; + private MdmLinkHistoryProviderDstu3Plus myLinkHistoryProvider; private final List myInterceptors = new ArrayList<>(); + @Override + @AfterEach + public void afterPurgeDatabase() { + super.afterPurgeDatabase(); + } + + @Override @BeforeEach public void before() throws Exception { super.before(); @@ -134,6 +144,7 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { ); } + @Override @AfterEach public void after() throws IOException { super.after(); @@ -141,8 +152,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { myInterceptors.clear(); } - @Nested - class MdmProviderDstu3PlusTest { @Test public void mergeGoldenResources_withInterceptor_firesHook() { // setup @@ -521,7 +530,7 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { } } } - } + private String createUrl(String theResourceType, StringType theCriteria) { String url = theResourceType; @@ -531,8 +540,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { return url; } - @Nested - class MdmLinkHistoryProviderDstu3PlusTest { @ParameterizedTest @EnumSource(LinkHistoryParameters.class) @@ -597,6 +604,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { assertTrue(called.get()); assertFalse(retval.isEmpty()); } - } + } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java index 8a899634fda..90001543085 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java @@ -37,7 +37,8 @@ public class MdmProviderCrossPartitionR4Test extends BaseProviderR4Test{ private static final String PARTITION_GOLDEN_RESOURCE = "PARTITION-GOLDEN"; - @BeforeEach + @Override + @BeforeEach public void before() throws Exception { super.before(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java index 967ccf4969c..57b19c41294 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java @@ -119,7 +119,6 @@ public class MdmMatchLinkSvcTest { assertLinksMatchVector((Long) null); } - @Test @RepeatedTest(20) public void testUpdatingAResourceToMatchACurrentlyUnmatchedResource_resultsInUpdatedLinksForBoth() { // setup diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java index da5734f574a..5cba1958901 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java @@ -9,7 +9,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.StopWatch; import org.apache.commons.dbcp2.BasicDataSource; -import org.hibernate.dialect.PostgreSQL9Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Disabled; @@ -115,9 +115,7 @@ public class MdmLinkSlowDeletionSandboxIT extends BaseJpaR4Test { @Override public String getHibernateDialect() { - return PostgreSQL9Dialect.class.getName(); - -// return Oracle12cDialect.class.getName(); + return PostgreSQLDialect.class.getName(); } @Override diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index e161b491e6b..8860e76228d 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -81,19 +81,19 @@ ${project.version} - org.hibernate + org.hibernate.orm hibernate-core org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.hibernate.search hibernate-search-backend-elasticsearch - org.hibernate + org.hibernate.orm hibernate-envers diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java new file mode 100644 index 00000000000..e38ff5d7878 --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DatabaseVersion; + +/** + * Dialect for CockroachDB database. + * Minimum version: 21.1 + */ +public class HapiFhirCockroachDialect extends CockroachDialect { + + public HapiFhirCockroachDialect() { + super(DatabaseVersion.make(21, 1)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java new file mode 100644 index 00000000000..b4e8f589f30 --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.DerbyDialect; + +/** + * Dialect for Oracle database. + * Minimum version: 10.14.2 + */ +public class HapiFhirDerbyDialect extends DerbyDialect { + + public HapiFhirDerbyDialect() { + super(DatabaseVersion.make(10, 14, 2)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java index 074bf229726..5cdf1eaad26 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java @@ -19,12 +19,13 @@ */ package ca.uhn.fhir.jpa.model.dialect; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import org.hibernate.dialect.DatabaseVersion; import org.hibernate.dialect.H2Dialect; -import java.sql.Types; - /** - * HAPI FHIR dialect for H2 database + * Dialect for H2 database. + * Minimum version: 2.2.220 */ public class HapiFhirH2Dialect extends H2Dialect { @@ -32,25 +33,33 @@ public class HapiFhirH2Dialect extends H2Dialect { * Constructor */ public HapiFhirH2Dialect() { - super(); - - /* - * These mappings are already defined in the super() constructor, but they - * will only happen if the dialect can connect to the database and - * determine that it's a recent enough version of H2 to support this. This - * means that the Maven plugin that does schema generation doesn't add it. - * So this dialect forces the use of the right defs. - */ - registerColumnType(Types.LONGVARCHAR, "character varying"); - registerColumnType(Types.BINARY, "binary($l)"); + super(DatabaseVersion.make(2, 2, 220)); } /** - * Workaround until this bug is fixed: - * https://hibernate.atlassian.net/browse/HHH-15002 + * As of Hibernate 6, generated schemas include a column level check constraint that enforces valid values + * for columns that back an Enum type. For example, the column definition for {@link ResourceTable#getFhirVersion()} + * would look like: + *
    +	 *  RES_VERSION varchar(7) check (RES_VERSION in ('DSTU2','DSTU2_HL7ORG','DSTU2_1','DSTU3','R4','R4B','R5')),
    +	 * 
    + *

    + * This is a nice addition since it enforces the values that the Enum allows, but it's problematic for us because these + * constraints are invisible to the JDBC metadata API on most databases, which means that our schema migration + * checker isn't able to catch problems if we add a value to an Enum and don't add a corresponding database + * migration. Rather than risk having inconsistent behaviour between annotated and migrated schemas, we just + * disable these checks on all of our dialects. + *

    + * See this discussion from the author of SchemaCrawler discussing this limitation: + * https://stackoverflow.com/questions/63346650/schemacrawler-java-api-retrieve-check-column-constraints. + * With this change in place, the definition above becomes simply: + *

    +	 *  RES_VERSION varchar(7),
    +	 * 
    + *

    */ @Override - public String toBooleanValueString(boolean bool) { - return bool ? "true" : "false"; + public boolean supportsColumnCheck() { + return false; } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java new file mode 100644 index 00000000000..0a121b8f3f5 --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.MariaDBDialect; + +/** + * Dialect for MySQL database. + * Minimum version: 10.11.5 + */ +public class HapiFhirMariaDBDialect extends MariaDBDialect { + + public HapiFhirMariaDBDialect() { + super(DatabaseVersion.make(10, 11, 5)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java new file mode 100644 index 00000000000..8bdbf26ba4c --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.MySQLDialect; + +/** + * Dialect for MySQL database. + * Minimum version: 5.7 + */ +public class HapiFhirMySQLDialect extends MySQLDialect { + + public HapiFhirMySQLDialect() { + super(DatabaseVersion.make(5, 7)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java new file mode 100644 index 00000000000..ec1095e3765 --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.OracleDialect; + +/** + * Dialect for Oracle database. + * Minimum version: 12.2 (Oracle 12c R2) + */ +public class HapiFhirOracleDialect extends OracleDialect { + + public HapiFhirOracleDialect() { + super(DatabaseVersion.make(12, 2)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java index 57f42b5322a..da3a48025ad 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java @@ -19,17 +19,21 @@ */ package ca.uhn.fhir.jpa.model.dialect; -import org.hibernate.dialect.PostgreSQL94Dialect; - -import java.sql.Types; +import org.hibernate.dialect.PostgreSQLDialect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This dialect is recommended when using HAPI FHIR JPA on Postgresql database. + * + * @deprecated Use {@link HapiFhirPostgresDialect} instead */ -public class HapiFhirPostgres94Dialect extends PostgreSQL94Dialect { +public class HapiFhirPostgres94Dialect extends PostgreSQLDialect { + private static final Logger ourLog = LoggerFactory.getLogger(HapiFhirPostgres94Dialect.class); public HapiFhirPostgres94Dialect() { super(); - registerColumnType(Types.CLOB, "oid"); + ourLog.warn("The " + getClass() + " dialect is deprecated and will be removed in a future release. Use " + + HapiFhirPostgresDialect.class.getName() + " instead"); } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java new file mode 100644 index 00000000000..c0a3435b7a1 --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java @@ -0,0 +1,38 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.PostgreSQLDialect; + +public class HapiFhirPostgresDialect extends PostgreSQLDialect { + + public HapiFhirPostgresDialect() { + super(DatabaseVersion.make(10, 0, 0)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java new file mode 100644 index 00000000000..606d91b4dea --- /dev/null +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 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.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.SQLServerDialect; + +/** + * Dialect for MS SQL Server database. + * Minimum version: 12.0 (SQL Server 2014 and Azure SQL Database) + */ +public class HapiFhirSQLServerDialect extends SQLServerDialect { + + public HapiFhirSQLServerDialect() { + super(DatabaseVersion.make(11)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java index 9e373c05e5d..cbdaa8a8aac 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java @@ -20,16 +20,19 @@ package ca.uhn.fhir.jpa.model.dialect; import ca.uhn.fhir.jpa.model.entity.StorageSettings; -import ca.uhn.fhir.util.ReflectionUtil; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; import org.apache.commons.lang3.Validate; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.Optimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.StandardOptimizerDescriptor; import org.hibernate.service.ServiceRegistry; @@ -45,7 +48,8 @@ import java.util.Properties; */ @SuppressWarnings("unused") public class HapiSequenceStyleGenerator - implements IdentifierGenerator, PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator { + implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, ExportableProducer { + public static final String ID_MASSAGER_TYPE_KEY = "hapi_fhir.sequence_generator_massager"; private final SequenceStyleGenerator myGen = new SequenceStyleGenerator(); @Autowired @@ -68,7 +72,7 @@ public class HapiSequenceStyleGenerator @Override public Serializable generate(SharedSessionContractImplementor theSession, Object theObject) throws HibernateException { - Long retVal = myIdMassager.generate(myGeneratorName); + Long retVal = myIdMassager != null ? myIdMassager.generate(myGeneratorName) : null; if (retVal == null) { Long next = (Long) myGen.generate(theSession, theObject); retVal = myIdMassager.massage(myGeneratorName, next); @@ -80,10 +84,9 @@ public class HapiSequenceStyleGenerator public void configure(Type theType, Properties theParams, ServiceRegistry theServiceRegistry) throws MappingException { - // Instantiate the ID massager - // StorageSettings should only be null when running in the DDL generation maven plugin - if (myStorageSettings != null) { - myIdMassager = ReflectionUtil.newInstance(myStorageSettings.getSequenceValueMassagerClass()); + myIdMassager = theServiceRegistry.getService(ISequenceValueMassager.class); + if (myIdMassager == null) { + myIdMassager = new ISequenceValueMassager.NoopSequenceValueMassager(); } // Create a HAPI FHIR sequence style generator @@ -91,9 +94,10 @@ public class HapiSequenceStyleGenerator Validate.notBlank(myGeneratorName, "No generator name found"); Properties props = new Properties(theParams); - props.put(SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName()); - props.put(SequenceStyleGenerator.INITIAL_PARAM, "1"); - props.put(SequenceStyleGenerator.INCREMENT_PARAM, "50"); + props.put(OptimizableGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName()); + props.put(OptimizableGenerator.INITIAL_PARAM, "1"); + props.put(OptimizableGenerator.INCREMENT_PARAM, "50"); + props.put(GENERATOR_NAME, myGeneratorName); myGen.configure(theType, props, theServiceRegistry); @@ -114,4 +118,9 @@ public class HapiSequenceStyleGenerator public boolean supportsJdbcBatchInserts() { return myGen.supportsJdbcBatchInserts(); } + + @Override + public Optimizer getOptimizer() { + return myGen.getOptimizer(); + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java index 473235d6e8d..8601fba3c21 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java @@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.MappedSuperclass; import org.hibernate.envers.Audited; import org.hibernate.envers.NotAudited; import java.io.Serializable; import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.MappedSuperclass; /** * This is a copy of (@link {@link BasePartitionable} used ONLY for entities that are audited by Hibernate Envers. 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 245abc493d9..646742cac02 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 @@ -23,17 +23,17 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.model.primitive.InstantDt; +import jakarta.persistence.Column; +import jakarta.persistence.EnumType; +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; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @MappedSuperclass public abstract class BaseHasResource extends BasePartitionable diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java index 0c3fe7ceed0..60f1d7962e5 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.MappedSuperclass; + import java.io.Serializable; import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.MappedSuperclass; /** * This is the base class for entities with partitioning that does NOT include Hibernate Envers logging. diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java index 9107f06862a..54a99aa9ccb 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.MappedSuperclass; import org.apache.commons.lang3.ObjectUtils; import java.io.Serializable; -import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class BaseResourceIndex extends BasePartitionable implements Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java index bdb31c75c8d..edcd363081a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java @@ -31,17 +31,17 @@ import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @MappedSuperclass public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java index ac1d27332b0..3850a31c010 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java @@ -21,12 +21,11 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.MappedSuperclass; - @MappedSuperclass public abstract class BaseResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearchParam { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java index bc4405d2d1a..27db242c506 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; + import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class BaseTag extends BasePartitionable implements Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java index d44d8e446f5..f2f9742b607 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java @@ -19,15 +19,16 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.sql.Blob; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; @Entity @Table(name = "HFJ_BINARY_STORAGE_BLOB") 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 c0aab1eb5b7..c77c5bf87ba 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 @@ -19,24 +19,23 @@ */ 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; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - @Entity() @Table( name = ForcedId.HFJ_FORCED_ID, @@ -44,6 +43,7 @@ import javax.persistence.UniqueConstraint; @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 diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java index 0c637ff84d5..acc24f6db0f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java @@ -19,23 +19,23 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; @Entity() @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java index 00eee555884..a102576c0f9 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java @@ -21,29 +21,28 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.util.StringUtil; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +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.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; @Entity() @Table( @@ -77,7 +76,7 @@ public class NpmPackageVersionEntity { @JoinColumn(name = "PACKAGE_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_NPM_PKV_PKG")) private NpmPackageEntity myPackage; - @OneToOne + @ManyToOne @JoinColumn( name = "BINARY_RES_ID", referencedColumnName = "RES_ID", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java index d9d9f0d8b5a..04428a7ceda 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java @@ -20,32 +20,30 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; @Entity() @Table( name = "NPM_PACKAGE_VER_RES", - uniqueConstraints = {}, indexes = { @Index(name = "IDX_PACKVERRES_URL", columnList = "CANONICAL_URL"), @Index(name = "FK_NPM_PACKVERRES_PACKVER", columnList = "PACKVER_PID"), @@ -67,7 +65,7 @@ public class NpmPackageVersionResourceEntity { nullable = false) private NpmPackageVersionEntity myPackageVersion; - @OneToOne + @ManyToOne @JoinColumn( name = "BINARY_RES_ID", referencedColumnName = "RES_ID", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java index 30c8660c1f7..e1a9026e9f7 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java @@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import java.time.LocalDate; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embeddable; @Embeddable public class PartitionablePartitionId implements Cloneable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java index 4cf7fa42516..a0fbf87d22a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java @@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; + import java.io.Serializable; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; @Embeddable public class PersistedResourceModifiedMessageEntityPK implements IPersistedResourceModifiedMessagePK, Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java index 754ab9b613c..66fd55a9a85 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java @@ -20,21 +20,20 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.rest.api.Constants; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MapsId; -import javax.persistence.OneToOne; -import javax.persistence.Table; - import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.SOURCE_URI_LENGTH; @Table( 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 ce53e3bb841..7e951765439 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 @@ -20,17 +20,18 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.OptimisticLock; +import org.hibernate.type.SqlTypes; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.*; @Entity @Table( @@ -91,7 +92,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl private byte[] myResource; @Column(name = "RES_TEXT_VC", length = RES_TEXT_VC_MAX_LENGTH, nullable = true) - @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE) + @JdbcTypeCode(SqlTypes.LONG32VARCHAR) @OptimisticLock(excluded = true) private String myResourceTextVc; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java index 21eb200e8aa..d77cf7c5538 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java @@ -19,20 +19,21 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; + import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; @Embeddable @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java index 1b386c23afb..898efaba2bc 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.*; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; @@ -27,8 +28,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IIdType; -import javax.persistence.*; - @Entity() @Table( name = "HFJ_IDX_CMP_STRING_UNIQ", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java index d4f96c2f7d0..75d6947c870 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java @@ -21,25 +21,24 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IIdType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; - import static ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam.hash; @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java index dbad03ccc81..505b36de92e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java @@ -21,25 +21,24 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - @Embeddable @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java index 01fcab6f275..4afac4201df 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java @@ -26,6 +26,22 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.util.DateUtils; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -37,22 +53,6 @@ import org.hl7.fhir.r4.model.DateTimeType; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @Embeddable @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java index e98508339ce..60c327ab767 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java @@ -22,27 +22,29 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.NumberParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; +import org.hibernate.type.SqlTypes; import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; @Embeddable @Entity @@ -57,8 +59,9 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP private static final long serialVersionUID = 1L; - @Column(name = "SP_VALUE", nullable = true) + @Column(name = "SP_VALUE", nullable = true, precision = 19, scale = 2) @ScaledNumberField + @JdbcTypeCode(SqlTypes.DECIMAL) public BigDecimal myValue; @Id diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java index 80841720449..7b988a19224 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java @@ -22,6 +22,19 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.QuantityParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -29,19 +42,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumb import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java index 2a7ca219558..7ba74219b4f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java @@ -23,6 +23,19 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.QuantityParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -31,19 +44,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumb import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java index 870c367a418..b5320e48cb0 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java @@ -25,24 +25,23 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.util.StringUtil; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; // @formatter:off diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java index 098f167a909..d8c44831239 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java @@ -24,6 +24,20 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.PrePersist; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -31,21 +45,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.PrePersist; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; @@ -299,6 +298,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + b.append("id", getId()); if (getPartitionId() != null) { b.append("partitionId", getPartitionId().getPartitionId()); } @@ -424,11 +424,11 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa return this; } - @PrePersist /** * We truncate the fields at the last moment because the tables have limited size. * We don't truncate earlier in the flow because the index hashes MUST be calculated on the full string. */ + @PrePersist public void truncateFieldsForDB() { mySystem = StringUtils.truncate(mySystem, MAX_LENGTH); myValue = StringUtils.truncate(myValue, MAX_LENGTH); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java index f2a2d8fe33c..aac0e778d12 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java @@ -23,26 +23,25 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.UriParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; @Embeddable diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index 8877dd90dbb..24ced82e9c4 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -19,6 +19,21 @@ */ 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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -27,21 +42,6 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.util.Date; import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java index 11ec35ff436..c7529a45270 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java @@ -20,14 +20,15 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; /** * This class describes how a resourceModifiedMessage is stored for later processing in the event where diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java index 681d20222f8..33b99846d2f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java @@ -19,14 +19,15 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; @Entity @Table( 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 bf1ca3b56ac..f51159ec306 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 @@ -28,6 +28,24 @@ import ca.uhn.fhir.jpa.model.search.SearchParamTextPropertyBinder; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +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; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.Session; @@ -57,24 +75,6 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.NamedEntityGraph; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.PostPersist; -import javax.persistence.PrePersist; -import javax.persistence.PreUpdate; -import javax.persistence.Table; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_TYPE_FHIR_ID; @@ -148,7 +148,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas private boolean myHasLinks; @Id - @GenericGenerator(name = "SEQ_RESOURCE_ID", strategy = "ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator") + @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID") @Column(name = "RES_ID") @GenericField(projectable = Projectable.YES) diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java index ba7f24a91ae..6c50caca82c 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java @@ -19,25 +19,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.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - @Entity @Table( name = "HFJ_RES_TAG", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java index b4ceebd9235..cd23d54ffe1 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.*; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -28,7 +29,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; -import javax.persistence.*; @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java index cf543b41520..fcb5c355d0e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.ParserOptions; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.dialect.ISequenceValueMassager; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; import ca.uhn.fhir.util.HapiExtensions; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java index 111c1c83e04..5f4ae0260b7 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java @@ -19,27 +19,29 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import java.io.Serializable; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; @Entity @Table( @@ -82,6 +84,7 @@ public class TagDefinition implements Serializable { @Column(name = "TAG_TYPE", nullable = false) @Enumerated(EnumType.ORDINAL) + @JdbcTypeCode(SqlTypes.INTEGER) private TagTypeEnum myTagType; @Column(name = "TAG_VERSION", length = 30) diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index 333a962df0d..60fc4b7b68d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -294,7 +294,6 @@ public class JpaConstants { */ public static final String SUMMARY_OPERATION_URL = "http://hl7.org/fhir/uv/ips/OperationDefinition/summary"; - public static final String ORG_HIBERNATE_TYPE_TEXT_TYPE = "org.hibernate.type.TextType"; public static final String BULK_META_EXTENSION_EXPORT_IDENTIFIER = "https://hapifhir.org/NamingSystem/bulk-export-identifier"; public static final String BULK_META_EXTENSION_JOB_ID = "https://hapifhir.org/NamingSystem/bulk-export-job-id"; diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 051a3069018..7668f21ed10 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -13,7 +13,7 @@ hapi-fhir-jpaserver-searchparam jar - HAPI FHIR Search Parameters + HAPI FHIR JPA - Search Parameters @@ -125,7 +125,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.jscience @@ -160,8 +160,8 @@ test - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api test diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java index 4f80dd9bbc2..c7884f8ad60 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java index 2ab2483c29f..8490bcabd03 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java index 0f6b2a61a44..3e6c8b5e96d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java index a3c7cddcea9..d44b20ce2d3 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java index 5cba0d76dd0..7afd92d97e0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java index fa6ba39cf72..e2144a84ddf 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java index 56c554c1ab8..d7297b2e453 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java index 47c39e0262a..2193b4c7f54 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java index 1efec3226db..a3eb41953ed 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java index 92e8f725db1..4f412bd5a6d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java index 131b5a6851d..09cf5b1de53 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java index 8b30b1af72d..bdd8ab4d164 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java index feec3df43ae..eba4a21f3ca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -23,6 +23,7 @@ import ca.uhn.fhir.IHapiBootOrder; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.ContextRefreshedEvent; @@ -30,8 +31,6 @@ import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - /** * This interceptor watches all resource changes on the server and compares them to the {@link IResourceChangeListenerCache} * entries. If the resource matches the resource type and search parameter map of that entry, then the corresponding cache diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java index c5ca4cffa6d..ca30c7decc1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java index 071dc8f1f20..ac01d8c0b54 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java index 6b54a10a5ab..ec741ff7ab2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java index 81b7b4f580f..225db5831c1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java index ea03d2cb32d..c2bfd3a67f1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java index f1d5a4a66b3..fcc2cc5e89b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java index 0114fba0b7a..78e0b475351 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java index e37924741bf..9801b1468b1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java index 9478efdab78..4aa0a28bbc0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index 0d5784a5818..c3d1ffa1ae1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java index 8aca1889e20..4ac537f3c2f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java index f1522621382..aa6c5233b40 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index 8ed714eb385..fdaf1eeafa1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -49,6 +49,7 @@ import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.bundle.BundleEntryParts; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -82,7 +83,6 @@ import java.util.TreeSet; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import javax.measure.quantity.Quantity; import javax.measure.unit.NonSI; import javax.measure.unit.Unit; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java index e7f9c352a24..643d26cef7b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java index 4d691e07b56..d5bc0957fab 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java index dee9d778828..fad69f1ffef 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java index fe137364848..29e3eb7b2e2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java index db6bf9bff5e..63f5e14f47d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java index a649c0601e1..027fb363c57 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java index dfd41f27635..2ca11dd124f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java index 3af3d4c2986..29b0baeef31 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java index 66d1bb1a047..983638e9e31 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java index a7e94c3ef19..f1e87737f5b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; @@ -32,7 +33,6 @@ import org.hl7.fhir.instance.model.api.IBase; import java.util.ArrayList; import java.util.List; -import javax.annotation.PostConstruct; public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implements ISearchParamExtractor { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java index 6b22d38b625..bc82a49e0ca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java index 6ff518529a4..4add6601f1f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java index 05ae9608475..44625b53a97 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -45,7 +46,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java index 01e288499c4..b8fddf42ea2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java index f091bdf3c1d..cc40da8191e 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java index 25366064cfd..b8fb38ebaca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java index 06cc6c57715..c7cf4e0fe07 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java index 829b369867e..4b3c145fb36 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java index 3deafea8642..64767055e00 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java index 75367cca4bc..1f73d8bf1e0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java index 3dcf5fec1e4..11081c200c7 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java index 41aed01c271..b5f8b513d94 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java index ac050c36b9c..de4fb681c85 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java index 35c9d14c4c2..c113b118b20 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java index d1866bfcb3a..b6d88cafefc 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java index a9c86f1fa8a..7bacb9317ae 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java index f0d4b49d379..939a654c860 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java index d8bd1b6b55d..1fb3fa28831 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -42,6 +42,8 @@ import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -59,8 +61,6 @@ import java.util.Optional; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java index f8b086965ca..cb2aa5ae87d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java index 9e6d8b204e9..40016c6c5ce 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java index a1041a1b738..77514845a41 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java index f7713bb924d..66bdb32cd8e 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java index caff223b6ea..4b86a7228af 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java index 5601d73a6e7..312079b585a 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java index ea566709e54..55e80e9d3ef 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java index 4ec77935492..7b183ff7657 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 9b119ecea50..03f4d2159b5 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -55,7 +55,7 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 org.springframework @@ -66,8 +66,8 @@ spring-context-support - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -93,11 +93,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - org.springframework.boot spring-boot-starter-test diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java index 40fd11b611c..f7ceb37b2ec 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java @@ -26,8 +26,8 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.simplejavamail.api.email.Email; import org.simplejavamail.email.EmailBuilder; import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.dialect.SpringStandardDialect; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.spring6.dialect.SpringStandardDialect; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.StringTemplateResolver; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java index 706d01ee16d..982ba3d9082 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java @@ -24,6 +24,8 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegi import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelWithHandlers; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; @@ -39,8 +41,6 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; public class SubscriptionWebsocketHandler extends TextWebSocketHandler implements WebSocketHandler { private static Logger ourLog = LoggerFactory.getLogger(SubscriptionWebsocketHandler.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java index b16b1874cd3..14fade327b2 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory; import ca.uhn.fhir.jpa.topic.SubscriptionTopicMatchingSubscriber; import ca.uhn.fhir.jpa.topic.SubscriptionTopicRegisteringSubscriber; +import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -34,8 +35,6 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; -import javax.annotation.PreDestroy; - import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber.SUBSCRIPTION_MATCHING_CHANNEL_NAME; public class MatchingQueueSubscriberLoader { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java index e791afa51e8..1a7742b13e4 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegi import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ChannelRetryConfiguration; import ca.uhn.fhir.util.HapiExtensions; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -39,7 +40,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; -import javax.annotation.PreDestroy; /** * Cache of active subscriptions. When a new subscription is added to the cache, a new Spring Channel is created diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java index 2f72cb60509..c905a4960b6 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.topic.SubscriptionTopicValidatingInterceptor; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu2.model.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Set; -import javax.annotation.PostConstruct; public class SubscriptionSubmitInterceptorLoader { private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionSubmitInterceptorLoader.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java index 55563d08c71..c32a2fadb6d 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java @@ -52,6 +52,7 @@ import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.collect.Lists; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; @@ -80,7 +81,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.rest.server.provider.ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID; import static java.util.Objects.isNull; diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java index 4c29d2b86ad..f78878cb75f 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java @@ -27,7 +27,7 @@ class BroadcastingSubscribableChannelWrapperTest { try { svc.send(new ResourceModifiedJsonMessage(new ResourceModifiedMessage())); } catch (MessageDeliveryException e) { - assertThat(e.getMessage(), containsString("Channel has zero subscribers")); + assertThat(e.getCause().getMessage(), containsString("Channel has zero subscribers")); } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java index 16789be59d3..5153c161776 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java @@ -79,7 +79,7 @@ public class BaseSubscriptionDeliverySubscriberTest { private SubscriptionDeliveringRestHookSubscriber mySubscriber; private SubscriptionDeliveringMessageSubscriber myMessageSubscriber; private SubscriptionDeliveringEmailSubscriber myEmailSubscriber; - private final FhirContext myCtx = FhirContext.forR4(); + private final FhirContext myCtx = FhirContext.forR4Cached(); @Mock private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -187,7 +187,7 @@ public class BaseSubscriptionDeliverySubscriberTest { mySubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload)); fail(); } catch (MessagingException e) { - assertEquals(Msg.code(2) + "Failure handling subscription payload for subscription: Subscription/123; nested exception is ca.uhn.fhir.rest.server.exceptions.InternalErrorException: FOO", e.getMessage()); + assertEquals(Msg.code(2) + "Failure handling subscription payload for subscription: Subscription/123", e.getMessage()); } verify(myGenericClient, times(1)).update(); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java index e46ba54cf6f..0912e517285 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java @@ -46,7 +46,7 @@ public class SubscriptionTestConfig { private IChannelNamer myChannelNamer; @Primary - @Bean(autowire = Autowire.BY_NAME, name = "myJpaValidationSupportChain") + @Bean(name = "myJpaValidationSupportChain") public IValidationSupport validationSupportChainR4() { return myFhirContext.getValidationSupport(); } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java index b081bbdb37f..c1d003c11c7 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java @@ -32,8 +32,8 @@ import ca.uhn.test.concurrency.IPointcutLatch; import ca.uhn.test.concurrency.PointcutLatch; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; @@ -51,7 +51,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.SubscribableChannel; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index 177675ad5a5..773adc6b312 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java index ae3d1ff799a..599a774b85f 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java @@ -6,9 +6,9 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index 15aa0f38c91..108a573859f 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -72,7 +72,7 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java index 57c472b280d..e5c828100a2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java index 12bfe63c89d..13537d4a6a0 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java @@ -82,7 +82,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java index a428d1fb818..3bbca30528c 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java @@ -8,8 +8,8 @@ import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -190,7 +190,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket() public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -200,14 +200,14 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -229,7 +229,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket() public class DynamicEchoSocket extends BaseSocket { private List myReceived = new ArrayList(); @@ -243,14 +243,14 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java index 615309cca5a..1771c4de9d3 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java @@ -37,8 +37,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java index 1a3509cd3f4..6e53d143310 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java @@ -18,8 +18,8 @@ import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java index 429a6cc69db..563b2627355 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java @@ -24,8 +24,8 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.AfterAll; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java index 6f993478058..454743f20d2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java @@ -23,8 +23,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 83dde3e9706..3af365157a4 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java index f9835681abc..65f79eba0d0 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java @@ -7,9 +7,9 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java index a20b849cacf..04c59ac49b9 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index db98eb767bd..83123192373 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -106,7 +106,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 7cda01d5905..2da47c1baed 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -2350,7 +2350,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { try { mySystemDao.transaction(mySrd, b); } catch (ResourceVersionConflictException e) { - assertEquals(Msg.code(550) + Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); + assertEquals(Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); } b = new Bundle(); diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java index 37e0a8ad6b8..e6c00f876bb 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java @@ -10,9 +10,10 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; @@ -21,6 +22,7 @@ import org.hl7.fhir.utilities.npm.PackageServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -47,36 +49,29 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { @Autowired @Qualifier(PackageUtils.LOADER_WITH_CACHE) private IHapiPackageCacheManager myPackageCacheManager; - private Server myServer; - private FakeNpmServlet myFakeNpmServlet; @Autowired private INpmPackageVersionDao myPackageVersionDao; - private int myPort; + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); + + @Override @BeforeEach public void before() throws Exception { super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - myPort = JettyUtil.getPortForStartedServer(myServer); + jpaPackageCache.getPackageServers().clear(); - jpaPackageCache.addPackageServer(new PackageServer("http://localhost:" + myPort)); + jpaPackageCache.addPackageServer(new PackageServer(myServer.getBaseUrl())); myFakeNpmServlet.getResponses().clear(); } @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences()); } @@ -156,7 +151,7 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("nictiz.fhir.nl.stu3.questionnaires") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); runInTransaction(() -> { @@ -240,7 +235,7 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("blah") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); fail(); } catch (InvalidRequestException e) { @@ -255,11 +250,11 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("blah") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); fail(); } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(1303) + "Received HTTP 404 from URL: http://localhost:" + myPort + "/foo.tgz", e.getMessage()); + assertEquals(Msg.code(1303) + "Received HTTP 404 from URL: " + myServer.getBaseUrl() + "/foo.tgz", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java index bee28e90161..0f293238783 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java @@ -1,35 +1,24 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.jpa.test.BaseJpaDstu3Test; -import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.Condition; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.utilities.npm.PackageServer; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -44,43 +33,34 @@ public class NpmDstu3Test extends BaseJpaDstu3Test { @Autowired private NpmJpaValidationSupport myNpmJpaValidationSupport; - private Server myServer; - private final Map myResponses = new HashMap<>(); + private ca.uhn.fhir.jpa.packages.FakeNpmServlet myFakeNpmServlet = new ca.uhn.fhir.jpa.packages.FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); + @Override @BeforeEach public void before() throws Exception { + super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - FakeNpmServlet fakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(fakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); jpaPackageCache.getPackageServers().clear(); - jpaPackageCache.addPackageServer(new PackageServer("http://localhost:" + port)); + jpaPackageCache.addPackageServer(new PackageServer(myServer.getBaseUrl())); - myResponses.clear(); + myFakeNpmServlet.getResponses().clear(); } - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } @Test public void installDstu3Package() throws Exception { byte[] bytes = ClasspathUtil.loadResourceAsByteArray("/packages/basisprofil.de.tar.gz"); - myResponses.put("/basisprofil.de/0.2.40", bytes); + myFakeNpmServlet.getResponses().put("/basisprofil.de/0.2.40", bytes); PackageInstallationSpec spec = new PackageInstallationSpec().setName("basisprofil.de").setVersion("0.2.40").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY); igInstaller.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); StructureDefinition sd = (StructureDefinition) myNpmJpaValidationSupport.fetchStructureDefinition("http://fhir.de/StructureDefinition/condition-de-basis/0.2"); assertEquals("http://fhir.de/StructureDefinition/condition-de-basis/0.2", sd.getUrl()); @@ -104,25 +84,4 @@ public class NpmDstu3Test extends BaseJpaDstu3Test { containsString("Condition.subject: minimum required = 1, but only found 0 (from http://fhir.de/StructureDefinition/condition-de-basis/0.2")); } - - private class FakeNpmServlet extends HttpServlet { - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String requestUrl = req.getRequestURI(); - if (myResponses.containsKey(requestUrl)) { - ourLog.info("Responding to request: {}", requestUrl); - - resp.setStatus(200); - resp.setHeader(Constants.HEADER_CONTENT_TYPE, "application/gzip"); - resp.getOutputStream().write(myResponses.get(requestUrl)); - resp.getOutputStream().close(); - } else { - ourLog.warn("Unknown request: {}", requestUrl); - - resp.sendError(404); - } - - } - } } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java index 95c777537f6..c716da163e4 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java @@ -5,8 +5,8 @@ import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3 import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.dstu3.model.Subscription; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType; @@ -179,7 +179,7 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class DynamicEchoSocket extends BaseSocket { private String myCriteria; @@ -193,14 +193,14 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -227,7 +227,7 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -237,14 +237,14 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java index 75f5a7301d7..93e904b44a7 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java @@ -16,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java index 43e66e09e3b..730cb5c13fb 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java @@ -26,8 +26,8 @@ import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.MetaUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; @@ -52,7 +52,7 @@ import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java index a4d4f22eff2..83a439228fc 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java @@ -15,8 +15,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java index 16961762a24..29eab34b330 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java @@ -23,8 +23,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; @@ -48,7 +48,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 79a1bddbeaa..f284432c58c 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -55,7 +55,12 @@ javax.json-api test - + + jakarta.servlet + jakarta.servlet-api + test + + @@ -78,10 +83,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - org.apache.maven.plugins maven-checkstyle-plugin diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java index 6c847de3250..bb7797a39fa 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java @@ -32,7 +32,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java index 6b69c725c0b..3e4062c5c67 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java @@ -26,9 +26,9 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Date; 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 c005497f6af..d9208ba6ab2 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 @@ -190,8 +190,8 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), - either(containsString("resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='B' and (resourceta0_.PARTITION_ID is null) or resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='A' and (resourceta0_.PARTITION_ID is null)")) - .or(containsString("resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='A' and (resourceta0_.PARTITION_ID is null) or resourceta0_.RES_TYPE='Patient' and resourceta0_.FHIR_ID='B' and (resourceta0_.PARTITION_ID is null)"))); + 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(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java index c05ea3cacf1..371091d3d62 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java @@ -30,15 +30,15 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.annotation.Nullable; -import javax.persistence.EntityExistsException; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityExistsException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; 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 98fea30536f..7fdd962e5c0 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 @@ -44,9 +44,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationContext; -import javax.persistence.EntityManager; -import java.util.ArrayList; -import java.util.Collection; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.stream.Stream; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java index 6bc21dff961..18237f46d50 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java @@ -38,8 +38,8 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java index c62a069d4c8..558555ad570 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java @@ -6,9 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java index 438667dfbb3..404cc2a639d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java @@ -5,8 +5,8 @@ import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.rest.server.RestfulServer; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; 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 457ba5e943d..77d17e0e3f0 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 @@ -26,6 +26,8 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants; 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.jpa.searchparam.submit.interceptor.SearchParamValidatingInterceptor; +import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionValidatingInterceptor; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc; import ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl; @@ -117,6 +119,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -592,7 +595,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test fail(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); } myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(11, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + assertEquals(12, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); @@ -1329,7 +1332,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '6'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -1345,7 +1348,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '6'")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -2405,9 +2408,9 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); // Make sure the match URL query uses a small limit - String matchUrlQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true); - assertThat(matchUrlQuery, containsString("HASH_SYS_AND_VALUE='-4132452001562191669'")); - assertThat(matchUrlQuery, containsString("limit '2'")); + String matchUrlQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false); + assertThat(matchUrlQuery, containsString("rispt1_0.HASH_SYS_AND_VALUE='-4132452001562191669'")); + assertThat(matchUrlQuery, containsString("fetch first '2'")); runInTransaction(() -> { List types = myResourceTableDao.findAll().stream().map(t -> t.getResourceType()).collect(Collectors.toList()); @@ -3206,7 +3209,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(7, expansion.getExpansion().getContains().size()); assertEquals(1, expansion.getExpansion().getContains().stream().filter(t -> t.getCode().equals("A")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getDesignation().size()); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(6, myCaptureQueriesListener.countSelectQueries(), ()->"\n *" + myCaptureQueriesListener.getSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -3249,7 +3252,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(7, expansion.getExpansion().getContains().size()); assertEquals(1, expansion.getExpansion().getContains().stream().filter(t -> t.getCode().equals("A")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getDesignation().size()); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(6, myCaptureQueriesListener.countSelectQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java index 0c26341ce74..a80c8002e3f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java @@ -24,7 +24,7 @@ import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 826f6ffad95..2d668f24e5d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -151,7 +151,7 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -3859,33 +3859,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertEquals(2, countMatches(searchQuery.toUpperCase(), "RES_UPDATED"), searchQuery); } - @Disabled - @Test - public void testSearchWithContext() { - - - String url = "Procedure?_count=300&_format=json&_include%3Arecurse=*&category=CANN&encounter.identifier=A1057852019&status%3Anot=entered-in-error"; - RuntimeResourceDefinition def = myFhirContext.getResourceDefinition("Procedure"); - SearchParameterMap sp = myMatchUrlService.translateMatchUrl(url, def); - - - myCaptureQueriesListener.clear(); - sp.setLoadSynchronous(true); - myProcedureDao.search(sp); - - myCaptureQueriesListener.logSelectQueriesForCurrentThread(); -// List queries = myCaptureQueriesListener -// .getSelectQueriesForCurrentThread() -// .stream() -// .map(t -> t.getSql(true, true)) -// .collect(Collectors.toList()); -// -// String searchQuery = queries.get(0); -// assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN")); -// assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN")); -// assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toUpperCase(), "AND RESOURCETA0_.RES_UPDATED")); - } - @Test public void testSearchTokenParam() { Patient patient = new Patient(); @@ -4619,8 +4592,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertNull(values.size()); assertEquals(5, values.getResources(0, 1000).size()); - String sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); - assertEquals(1, countMatches(sql, "limit '5'"), sql); + String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertEquals(1, countMatches(sql, "fetch first '5'"), sql); } @Test 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 0f63fe5d3b5..1e15bc49293 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 @@ -96,9 +96,11 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { @Autowired private MatchUrlService myMatchUrlService; + @Override @BeforeEach - public void before() { - mySearchCoordinatorSvcImpl = (SearchCoordinatorSvcImpl) ProxyUtil.getSingletonTarget(mySearchCoordinatorSvc, SearchCoordinatorSvcImpl.class); + public void before() throws Exception { + super.before(); + mySearchCoordinatorSvcImpl = ProxyUtil.getSingletonTarget(mySearchCoordinatorSvc, SearchCoordinatorSvcImpl.class); mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(QueryParameterUtils.DEFAULT_SYNC_SIZE); myCaptureQueriesListener.setCaptureQueryStackTrace(true); @@ -894,12 +896,12 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { myCaptureQueriesListener.logSelectQueriesForCurrentThread(); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.res_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.fhir_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id in ('a')"), selectQuery); selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "select t1.res_id from hfj_resource t1"), selectQuery); - assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_type = 'observation'"), selectQuery); + assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_type='observation'"), selectQuery); assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_deleted_at is null"), selectQuery); } @@ -936,8 +938,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.res_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "resourceta0_.fhir_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id in ('a')"), selectQuery); } // Search by ID where at least one ID is a numeric ID @@ -1546,7 +1548,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { // Forced ID resolution resultingQueryNotFormatted = queries.get(0); assertThat(resultingQueryNotFormatted, containsString("RES_TYPE='Organization'")); - assertThat(resultingQueryNotFormatted, containsString("resourceta0_.RES_TYPE='Organization' and resourceta0_.FHIR_ID='ORG1' or resourceta0_.RES_TYPE='Organization' and resourceta0_.FHIR_ID='ORG2'")); + assertThat(resultingQueryNotFormatted, containsString("rt1_0.RES_TYPE='Organization' and rt1_0.FHIR_ID='ORG1' or rt1_0.RES_TYPE='Organization' and rt1_0.FHIR_ID='ORG2'")); // The search itself resultingQueryNotFormatted = queries.get(1); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java index 0ccd6ae7d0e..710639e34fb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java @@ -99,10 +99,10 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 INNER JOIN HFJ_RES_TAG t1 ON (t0.RES_ID = t1.RES_ID) INNER JOIN HFJ_TAG_DEF t2 ON (t1.TAG_ID = t2.TAG_ID) WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t2.TAG_TYPE = ?) AND (t2.TAG_SYSTEM = ?) AND (t2.TAG_CODE = ?)))", sql); // Query 2 - Load resourece contents sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(false, false); - assertThat(sql, containsString("where resourcese0_.RES_ID in (?)")); + assertThat(sql, containsString("where rsv1_0.RES_ID in (?)")); // Query 3 - Load tags and defintions sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(false, false); - assertThat(sql, containsString("from HFJ_RES_TAG resourceta0_ inner join HFJ_TAG_DEF")); + assertThat(sql, containsString("from HFJ_RES_TAG rt1_0 join HFJ_TAG_DEF")); assertThat(toUnqualifiedVersionlessIds(outcome), Matchers.contains(id)); @@ -138,7 +138,7 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_URI t0 WHERE (t0.HASH_URI = ?)", sql); // Query 2 - Load resourece contents sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(false, false); - assertThat(sql, containsString("where resourcese0_.RES_ID in (?)")); + assertThat(sql, containsString("where rsv1_0.RES_ID in (?)")); assertThat(toUnqualifiedVersionlessIds(outcome), Matchers.contains(id)); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java index 44ae538e139..4f04779cdb8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java @@ -45,7 +45,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javax.persistence.Id; +import jakarta.persistence.Id; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index 671416c2cba..988421f7cc7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -4008,7 +4008,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { try { mySystemDao.transaction(mySrd, b); } catch (ResourceVersionConflictException e) { - assertEquals(Msg.code(550) + Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); + assertEquals(Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); } b = new Bundle(); 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 eabd086087b..42004dba52f 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 @@ -876,7 +876,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 as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); } { @@ -888,7 +888,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 as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); } } @@ -910,7 +910,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 as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID='1'"), searchSql); } @@ -954,7 +954,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 as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -967,7 +967,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 as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1008,7 +1008,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 as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -1022,7 +1022,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 as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1064,7 +1064,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 as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null")); } @@ -2827,8 +2827,8 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("esourcein0_.PARTITION_ID in ('1')")); - assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("HASH_SYS_AND_VALUE in ('7432183691485874662' , '-3772330830566471409'")); + assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("rispt1_0.PARTITION_ID in ('1')")); + assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("rispt1_0.HASH_SYS_AND_VALUE in ('7432183691485874662','-3772330830566471409','-4132452001562191669')")); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(45, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); @@ -2986,12 +2986,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID in"), searchSql); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID in"), searchSql); // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID in"), searchSql.replace(" ", "").toUpperCase()); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID in"), searchSql.replace(" ", "").toUpperCase()); } @Test @@ -3103,12 +3103,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("SQL:{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true)); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase(); assertEquals(1, countMatches(sql, "COUNT("), sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); // Fetch history sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); } @@ -3134,7 +3134,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Count String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID is null"), searchSql); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID is null"), searchSql); // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); @@ -3213,12 +3213,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); assertEquals(1, countMatches(sql, "COUNT("), sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); // History sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); } @@ -3244,12 +3244,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Resolve resource String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toUpperCase(); - assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL")); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IS NULL"), sql); assertEquals(1, countMatches(sql, "PARTITION_ID")); // Fetch history resource sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true).toUpperCase(); - assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL")); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IS NULL"), sql); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java index 8492091e342..b53ac1ef305 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java @@ -30,8 +30,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; -import javax.persistence.Query; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.Query; import java.util.Date; import java.util.List; import java.util.stream.Stream; @@ -448,6 +448,9 @@ public class ReindexJobTest extends BaseJpaR4Test { @Test public void testReindex_withReindexingUponSearchParameterChangeEnabled_reindexJobCompleted() { + List jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); + assertEquals(0, jobInstances.size()); + // make sure the resources auto-reindex after the search parameter update is enabled myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(true); @@ -456,7 +459,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myReindexTestHelper.createCodeSearchParameter(); // check that reindex job was created - List jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); + jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); assertEquals(1, jobInstances.size()); // check that the job is completed (not stuck in QUEUED status) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java index 211b2456d85..e990c57ef1d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java @@ -30,8 +30,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Encounter; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java index 945f6f19472..9892649972c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java @@ -63,7 +63,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '6' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -88,7 +88,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '6'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '6' rows only")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -149,7 +149,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '8' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -172,7 +172,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '8'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '8' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java index 8bf59f3fd96..9e80739a522 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java @@ -39,7 +39,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; 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 75eb2f79d01..0e432bbcee5 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 @@ -181,8 +181,8 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertTrue(patient.getActive()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("resourceta0_.PARTITION_ID in (?)")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("where resourceta0_.PARTITION_ID=? and resourceta0_.RES_ID=?")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("rt1_0.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("where rt1_0.PARTITION_ID=? and rt1_0.RES_ID=?")); } @Test @@ -229,7 +229,7 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertEquals(1, outcome.size()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("resourceta0_.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("rt1_0.PARTITION_ID in (?)")); assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("t0.PARTITION_ID = ?")); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java index 799e6133124..a6a129549e0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java @@ -26,12 +26,13 @@ import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInter import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.validation.ValidationResult; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Enumerations; @@ -51,6 +52,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -90,18 +92,20 @@ public class NpmR4Test extends BaseJpaR4Test { private IHapiPackageCacheManager myPackageCacheManager; @Autowired private NpmJpaValidationSupport myNpmJpaValidationSupport; - private Server myServer; @Autowired private INpmPackageDao myPackageDao; @Autowired private INpmPackageVersionDao myPackageVersionDao; @Autowired private INpmPackageVersionResourceDao myPackageVersionResourceDao; - private FakeNpmServlet myFakeNpmServlet; @Autowired private IInterceptorService myInterceptorService; @Autowired private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor; + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); @Override @BeforeEach @@ -110,17 +114,8 @@ public class NpmR4Test extends BaseJpaR4Test { JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); jpaPackageCache.getPackageServers().clear(); - String url = "http://localhost:" + port; + String url = myServer.getBaseUrl(); ourLog.info("Package server is at base: {}", url); jpaPackageCache.addPackageServer(new PackageServer(url)); @@ -129,7 +124,6 @@ public class NpmR4Test extends BaseJpaR4Test { @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences()); myStorageSettings.setAutoCreatePlaceholderReferenceTargets(new JpaStorageSettings().isAutoCreatePlaceholderReferenceTargets()); myPartitionSettings.setPartitioningEnabled(false); @@ -219,7 +213,7 @@ public class NpmR4Test extends BaseJpaR4Test { myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("nictiz.fhir.nl.stu3.questionnaires", "1.0.2"); @@ -262,7 +256,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0"); @@ -332,7 +326,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0"); @@ -402,7 +396,7 @@ public class NpmR4Test extends BaseJpaR4Test { PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.13.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { @@ -431,7 +425,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(3, outcome.getResourcesInstalled().get("Organization")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources runInTransaction(() -> { @@ -471,7 +465,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(3, outcome.getResourcesInstalled().get("Organization")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources mySrd = mock(ServletRequestDetails.class); @@ -540,7 +534,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("ImplementationGuide")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources runInTransaction(() -> { @@ -621,7 +615,7 @@ public class NpmR4Test extends BaseJpaR4Test { myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("UK.Core.r4", "1.1.0"); @@ -930,7 +924,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getResourcesInstalled().get("StructureDefinition")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { @@ -967,7 +961,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getResourcesInstalled().get("StructureDefinition")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java index b8c8a28d8ca..aa06e958181 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java @@ -36,8 +36,10 @@ public class NpmSearchR4Test extends BaseJpaR4Test { @Autowired private INpmPackageVersionResourceDao myPackageVersionResourceDao; + @Override @BeforeEach public void before() throws Exception { + super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); jpaPackageCache.getPackageServers().clear(); } @@ -155,7 +157,7 @@ public class NpmSearchR4Test extends BaseJpaR4Test { search = myPackageCacheManager.search(searchSpec); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search)); + ourLog.info("Search results:\r{}", JsonUtil.serialize(search)); assertEquals(1, search.getTotal()); assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName()); assertEquals("4.0.1", search.getObjects().get(0).getPackage().getFhirVersion().get(0)); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java index b6df9a61732..3c26a4ec46f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java @@ -4,16 +4,18 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.packages.FakeNpmServlet; import ca.uhn.fhir.jpa.packages.util.PackageUtils; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; import org.mockito.Spy; @@ -32,40 +34,23 @@ public class PackageLoaderSvcIT { @Spy private FhirContext myFhirContext = FhirContext.forR4Cached(); + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + private PackageLoaderSvc myPackageLoaderSvc = new PackageLoaderSvc(); + private PackageResourceParsingSvc myResourceParsingSvc = new PackageResourceParsingSvc(myFhirContext); - private Server myServer; - - private FakeNpmServlet myFakeNpmServlet; - - private PackageLoaderSvc myPackageLoaderSvc; - - private PackageResourceParsingSvc myResourceParsingSvc; + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); @BeforeEach public void before() throws Exception { - myPackageLoaderSvc = new PackageLoaderSvc(); - myResourceParsingSvc = new PackageResourceParsingSvc(myFhirContext); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); myPackageLoaderSvc.getPackageServers().clear(); - myPackageLoaderSvc.addPackageServer(new PackageServer("http://localhost:" + port)); + myPackageLoaderSvc.addPackageServer(new PackageServer(myServer.getBaseUrl())); myFakeNpmServlet.getResponses().clear(); } - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } - @Test public void fetchPackageFromServer_thenParseoutResources_inMemory() throws IOException { // setup diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java index 1def4fbd776..cfec106c08a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java index 8619423aa9c..f84f607ef89 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java @@ -15,8 +15,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Observation; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 139f4df1e5a..e96a313c7a4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -54,7 +54,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.springframework.mock.web.MockHttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java index a02443b3f09..4e543edd508 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java @@ -27,7 +27,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java index d5ac558d272..f6b9cd4b9df 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java @@ -401,6 +401,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE); mySearchParameterDao.create(fooSp, mySrd); + mySearchParamRegistry.forceRefresh(); + assertNotNull(mySearchParamRegistry.getActiveSearchParam("Patient", "foo")); + Patient pat = new Patient(); pat.setGender(AdministrativeGender.MALE); IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); @@ -547,8 +550,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide ourLog.info("Found: {}", found); runInTransaction(() -> { - List currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); - List currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList(); + + List currentResults = myEntityManager.createNativeQuery("select distinct r1_0.RES_ID as col_0_0_ from HFJ_RESOURCE r1_0 left outer join HFJ_SPIDX_STRING myparamsst1_ on r1_0.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); + List currentResources = myEntityManager.createNativeQuery("select r1_0.RES_ID as col_0_0_ from HFJ_RESOURCE r1_0").getResultList(); List searches = mySearchEntityDao.findAll(); assertEquals(1, searches.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java index 2e17f8d2780..7f3a0e06819 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java @@ -46,7 +46,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java index a1d3c5fd1bf..cc8a4b237ba 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java @@ -13,8 +13,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java index b2cfe855dbd..0198b2c7ee9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index 722cc8ae8e1..b0a5e6ed236 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; @@ -369,15 +370,20 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { IFhirResourceDao searchParameterDao = myDaoRegistry.getResourceDao(SearchParameter.class); searchParameterDao.create(searchParameter, (RequestDetails) null); + RuntimeSearchParam sp = mySearchParamRegistry.getActiveSearchParam("Organization", "_profile"); + assertNotNull(sp); + IFhirResourceDao organizationDao = myDaoRegistry.getResourceDao(Organization.class); Organization organizationWithNoProfile = new Organization(); organizationWithNoProfile.setName("noProfile"); - organizationDao.create(organizationWithNoProfile); + organizationDao.create(organizationWithNoProfile, mySrd); + myCaptureQueriesListener.clear(); Organization organizationWithProfile = new Organization(); organizationWithProfile.setName("withProfile"); organizationWithProfile.getMeta().addProfile("http://foo"); - organizationDao.create(organizationWithProfile); + organizationDao.create(organizationWithProfile, mySrd); + myCaptureQueriesListener.logInsertQueries(); runInTransaction(() -> { List matched = myResourceIndexedSearchParamUriDao.findAll().stream() @@ -942,12 +948,14 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { ourLog.info("About to perform search for: {}", theUri); + myCaptureQueriesListener.clear(); try (CloseableHttpResponse response = ourHttpClient.execute(get)) { String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); ids = toUnqualifiedIdValues(bundle); } + myCaptureQueriesListener.logSelectQueries(true, true); return ids; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java index 7b69cc6a741..35e96b1ea41 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java @@ -1075,7 +1075,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv .execute(); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA")); - assertEquals(12, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(14, myCaptureQueriesListener.getSelectQueries().size(), ()->myCaptureQueriesListener.logSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals("ValueSet \"ValueSet.url[http://example.com/my_value_set]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); // Hierarchical @@ -1092,7 +1092,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains()), containsInAnyOrder("AA", "AB")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains().stream().filter(t -> t.getCode().equals("AA")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getContains()), containsInAnyOrder("AAA")); - assertEquals(13, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(15, myCaptureQueriesListener.getSelectQueries().size()); } @@ -1115,7 +1115,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv .execute(); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA")); - assertEquals(8, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(10, myCaptureQueriesListener.getSelectQueries().size(), ()->myCaptureQueriesListener.logSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals("ValueSet with URL \"Unidentified ValueSet\" was expanded using an in-memory expansion", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); // Hierarchical @@ -1132,7 +1132,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains()), containsInAnyOrder("AA", "AB")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains().stream().filter(t -> t.getCode().equals("AA")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getContains()), containsInAnyOrder("AAA")); - assertEquals(11, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(13, myCaptureQueriesListener.getSelectQueries().size()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java index d9a45aa3658..889027665a8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java @@ -6,8 +6,8 @@ import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorR4; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -186,7 +186,7 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class DynamicEchoSocket extends BaseSocket { private String myCriteria; @@ -200,14 +200,14 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -234,7 +234,7 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -244,14 +244,14 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java index 13a54906716..2e7b101b234 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java @@ -57,8 +57,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IIdType; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java index 016e3de04e6..5a293f5610a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java @@ -20,8 +20,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleType; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java index ef4e4b36e2b..4aceb1748cc 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java @@ -79,20 +79,20 @@ import org.springframework.transaction.support.TransactionSynchronizationManager import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.FlushModeType; -import javax.persistence.LockModeType; -import javax.persistence.Query; -import javax.persistence.StoredProcedureQuery; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaDelete; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.CriteriaUpdate; -import javax.persistence.metamodel.Metamodel; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Query; +import jakarta.persistence.StoredProcedureQuery; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Metamodel; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -181,6 +181,7 @@ public class GiantTransactionPerfTest { myHapiTransactionService.setTransactionManager(myTransactionManager); myHapiTransactionService.setInterceptorBroadcaster(myInterceptorSvc); myHapiTransactionService.setRequestPartitionSvcForUnitTest(myRequestPartitionHelperSvc); + myHapiTransactionService.setPartitionSettingsForUnitTest(new PartitionSettings()); myTransactionProcessor = new TransactionProcessor(); myTransactionProcessor.setContext(ourFhirContext); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java index 1b315cdca30..cd9eeecf4b2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java index b4f22254adb..f5e504131f4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java @@ -13,8 +13,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java index 1cd12d66bce..3410c4877a7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java @@ -15,8 +15,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java index 43dd37135fb..95f6f583f31 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java @@ -49,8 +49,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Pageable; import org.springframework.test.util.ReflectionTestUtils; -import javax.persistence.EntityManager; -import javax.persistence.NonUniqueResultException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NonUniqueResultException; import javax.sql.DataSource; import java.util.Arrays; import java.util.Collections; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java index 17cdf197835..e3d7d61c031 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java @@ -39,8 +39,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.ClassPathResource; -import javax.persistence.EntityManager; -import javax.servlet.http.HttpServletResponse; +import jakarta.persistence.EntityManager; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java index 92a19636014..623c94d6f34 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java @@ -34,7 +34,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.util.ResourceUtils; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index df49d1e866a..e2d784822f9 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 - 6.11.4-SNAPSHOT + 6.11.5-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 d3268b3f732..0a275e83112 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 @@ -124,7 +124,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.List; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java index bd551395c22..d645cd43e0f 100644 --- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 3e7ea400eca..ec884ead8bb 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 - 6.11.4-SNAPSHOT + 6.11.5-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 aa65349989c..1ef5161e6ab 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 @@ -133,7 +133,7 @@ import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; 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 fcd1bf9dc0e..308d97c70f6 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 @@ -92,7 +92,7 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { // One select to resolve the 3 match URLs assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); String firstSelectQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false); - assertEquals(1, countMatches(firstSelectQuery, "HASH_SYS_AND_VALUE in (? , ? , ? , ?)"), firstSelectQuery); + assertEquals(1, countMatches(firstSelectQuery, "rispt1_0.HASH_SYS_AND_VALUE in (?,?,?,?)"), firstSelectQuery); assertEquals(23, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(3, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java index a39f187b5ae..ea35ecdf076 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java @@ -27,8 +27,8 @@ import ca.uhn.test.concurrency.PointcutLatch; import net.ttddyy.dsproxy.QueryCount; import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.model.Bundle; @@ -45,8 +45,8 @@ import org.junit.jupiter.api.Disabled; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index a1bf33ca001..8b56c9eac09 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -85,42 +85,6 @@ org.apache.derby derby - - org.eclipse.jetty - jetty-servlets - - - org.eclipse.jetty - jetty-servlet - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-client - - - org.eclipse.jetty.websocket - websocket-jetty-server - org.springframework.boot spring-boot-test @@ -215,17 +179,21 @@ mockito-core - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.testcontainers junit-jupiter + + jakarta.websocket + jakarta.websocket-client-api + com.jayway.jsonpath diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java index 4b09d4fd13c..697f6a96f7c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.embedded; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.test.utilities.docker.DockerRequiredCondition; import ca.uhn.fhir.util.VersionEnum; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; @@ -51,20 +52,24 @@ public class HapiEmbeddedDatabasesExtension implements AfterAllCallback { private final DatabaseInitializerHelper myDatabaseInitializerHelper = new DatabaseInitializerHelper(); public HapiEmbeddedDatabasesExtension() { - myEmbeddedDatabases.add(new H2EmbeddedDatabase()); - myEmbeddedDatabases.add(new PostgresEmbeddedDatabase()); - myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase()); - if (canUseOracle()) { - myEmbeddedDatabases.add(new OracleEmbeddedDatabase()); + if (DockerRequiredCondition.isDockerAvailable()) { + myEmbeddedDatabases.add(new H2EmbeddedDatabase()); + myEmbeddedDatabases.add(new PostgresEmbeddedDatabase()); + myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase()); + if (canUseOracle()) { + myEmbeddedDatabases.add(new OracleEmbeddedDatabase()); + } else { + String message = + "Cannot add OracleEmbeddedDatabase. If you are using a Mac you must configure the TestContainers API to run using Colima (https://www.testcontainers.org/supported_docker_environment#using-colima)"; + ourLog.warn(message); + } } else { - String message = - "Cannot add OracleEmbeddedDatabase. If you are using a Mac you must configure the TestContainers API to run using Colima (https://www.testcontainers.org/supported_docker_environment#using-colima)"; - ourLog.warn(message); + ourLog.warn("Docker is not available! Not going to start any embedded databases."); } } @Override - public void afterAll(ExtensionContext theExtensionContext) throws Exception { + public void afterAll(ExtensionContext theExtensionContext) { for (JpaEmbeddedDatabase database : getAllEmbeddedDatabases()) { database.stop(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java index 588ffa0cb2e..256c57078a9 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java @@ -25,9 +25,9 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.http.HttpServletRequest; import java.util.Set; -import javax.servlet.http.HttpServletRequest; // This class is replicated in PartitionExamples.java -- Keep it up to date there too!! @Interceptor diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java index 7a0e28df80c..dbc29a6d8bf 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java @@ -20,6 +20,9 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.rest.api.Constants; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +30,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class FakeNpmServlet extends HttpServlet { private static final Logger ourLog = LoggerFactory.getLogger(FakeNpmServlet.class); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java index feedbd05f0e..baf2b8c623f 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.provider; import org.junit.jupiter.params.provider.Arguments; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index 943fa6bbd81..7e4b86df570 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -49,9 +49,9 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Parameters; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java index 825c0edff25..780eab57d4d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java @@ -19,15 +19,16 @@ */ package ca.uhn.fhir.jpa.subscription; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Receives subscription notification without payloads. diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java index c4e67c55b92..2d52a950cd2 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java @@ -20,17 +20,17 @@ package ca.uhn.fhir.jpa.subscription; import ca.uhn.fhir.rest.api.EncodingEnum; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.List; -@WebSocket +@ClientEndpoint public class SocketImplementation { private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class); @@ -53,7 +53,7 @@ public class SocketImplementation { public void keepAlive() { if (this.session != null) { try { - session.getRemote().sendString("keep alive"); + session.getBasicRemote().sendText("keep alive"); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -66,14 +66,14 @@ public class SocketImplementation { * * @param session */ - @OnWebSocketConnect + @OnOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.getBasicRemote().sendText(sending); ourLog.info("Connection: DONE"); } catch (Throwable t) { @@ -87,7 +87,7 @@ public class SocketImplementation { * * @param theMsg */ - @OnWebSocketMessage + @OnMessage public void onMessage(String theMsg) { ourLog.info("Got msg: " + theMsg); myMessages.add(theMsg); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java index a699670da89..b43ccc2172e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java @@ -129,7 +129,7 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.Map; @ExtendWith(SpringExtension.class) 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 a0d630c9b12..68813b7c4d5 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,6 +20,8 @@ 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; @@ -39,6 +41,8 @@ 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.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; @@ -98,6 +102,7 @@ import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.ValidationSettings; +import ca.uhn.fhir.mdm.interceptor.MdmStorageInterceptor; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; @@ -199,7 +204,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -240,6 +245,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil protected ITermCodeSystemStorageSvc myTermCodeSystemStorageSvc; @Autowired protected ISearchDao mySearchEntityDao; + @Autowired + private IBatch2JobInstanceRepository myJobInstanceRepository; + @Autowired + private IBatch2WorkChunkRepository myWorkChunkRepository; @Autowired protected ISearchIncludeDao mySearchIncludeEntityDao; @@ -533,6 +542,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil private IBulkDataExportJobSchedulingHelper myBulkDataScheduleHelper; @Autowired protected IResourceSearchUrlDao myResourceSearchUrlDao; + @Autowired + private IInterceptorService myInterceptorService; + @Autowired(required = false) + private MdmStorageInterceptor myMdmStorageInterceptor; @RegisterExtension private final PreventDanglingInterceptorsExtension myPreventDanglingInterceptorsExtension = new PreventDanglingInterceptorsExtension(()-> myInterceptorRegistry); @@ -594,11 +607,28 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @AfterEach public void afterPurgeDatabase() { - runInTransaction(() -> { - myMdmLinkHistoryDao.deleteAll(); - myMdmLinkDao.deleteAll(); - }); - purgeDatabase(myStorageSettings, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper); + boolean registeredStorageInterceptor = false; + if (myMdmStorageInterceptor != null && !myInterceptorService.getAllRegisteredInterceptors().contains(myMdmStorageInterceptor)) { + myInterceptorService.registerInterceptor(myMdmStorageInterceptor); + registeredStorageInterceptor = true; + } + try { + runInTransaction(() -> { + myMdmLinkHistoryDao.deleteAll(); + myMdmLinkDao.deleteAll(); + }); + purgeDatabase(myStorageSettings, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper); + + myBatch2JobHelper.cancelAllJobsAndAwaitCancellation(); + runInTransaction(() -> { + myWorkChunkRepository.deleteAll(); + myJobInstanceRepository.deleteAll(); + }); + } finally { + if (registeredStorageInterceptor) { + myInterceptorService.unregisterInterceptor(myMdmStorageInterceptor); + } + } } @BeforeEach 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 9edc49122a3..4dcc9189da1 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 @@ -130,7 +130,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.time.Duration; import java.time.temporal.ChronoUnit; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java index bc8facbfec4..fb7b241ebc5 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java @@ -76,7 +76,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java index ad9e8ebf4ce..39ab80ce66f 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.JpaDstu2Config; @@ -57,6 +58,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -95,7 +97,8 @@ public class TestDstu2Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -169,8 +172,8 @@ public class TestDstu2Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu2"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java index 08515a33a31..521aa935c90 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.PackageLoaderConfig; @@ -61,6 +62,7 @@ import java.sql.Connection; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -82,7 +84,8 @@ public class TestDstu3Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -180,8 +183,8 @@ public class TestDstu3Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu3"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java index eefbc9e8139..532f8636507 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java @@ -44,7 +44,7 @@ import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.testcontainers.elasticsearch.ElasticsearchContainer; -import javax.annotation.PreDestroy; +import jakarta.annotation.PreDestroy; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java index 5ce4a94655d..10fb896beed 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 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.test.config; import ca.uhn.fhir.jpa.config.HapiJpaConfig; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java index 4c334819646..716ceccd079 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java @@ -47,7 +47,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; @Configuration @Import({ diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java index e6473f15647..0cf3d61f1a0 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; @@ -64,6 +65,7 @@ import java.util.LinkedList; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -106,7 +108,8 @@ public class TestR4BConfig { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -212,8 +215,8 @@ public class TestR4BConfig { @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java index 98942ba4d5a..d61a4670331 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; @@ -52,12 +53,20 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -75,6 +84,8 @@ public class TestR4Config { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class); public static Integer ourMaxThreads; + private final AtomicInteger myBorrowedConnectionCount = new AtomicInteger(0); + private final AtomicInteger myReturnedConnectionCount = new AtomicInteger(0); static { /* @@ -95,14 +106,18 @@ public class TestR4Config { ourLog.warn("ourMaxThreads={}", ourMaxThreads); } - private final Deque myLastStackTrace = new LinkedList<>(); + private Map myConnectionRequestStackTraces = Collections.synchronizedMap(new LinkedHashMap<>()); + @Autowired TestHSearchAddInConfig.IHSearchConfigurer hibernateSearchConfigurer; private boolean myHaveDumpedThreads; + @Autowired + private JpaStorageSettings myStorageSettings; @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -116,6 +131,7 @@ public class TestR4Config { retVal = new ConnectionWrapper(super.getConnection()); } catch (Exception e) { ourLog.error("Exceeded maximum wait for connection (" + ourMaxThreads + " max)", e); + ourLog.info("Have {} outstanding - {} borrowed {} returned", (myBorrowedConnectionCount.get() - myReturnedConnectionCount.get()), myBorrowedConnectionCount.get(), myReturnedConnectionCount.get()); logGetConnectionStackTrace(); fail("Exceeded maximum wait for connection (" + ourMaxThreads + " max): " + e); retVal = null; @@ -124,38 +140,42 @@ public class TestR4Config { try { throw new Exception(); } catch (Exception e) { - synchronized (myLastStackTrace) { - myLastStackTrace.add(e); - while (myLastStackTrace.size() > ourMaxThreads) { - myLastStackTrace.removeFirst(); - } - } + myConnectionRequestStackTraces.put(retVal, e); } - return retVal; + myBorrowedConnectionCount.incrementAndGet(); + ConnectionWrapper finalRetVal = retVal; + return new ConnectionWrapper(finalRetVal){ + @Override + public void close() throws SQLException { + myConnectionRequestStackTraces.remove(finalRetVal); + myReturnedConnectionCount.incrementAndGet(); + super.close(); + } + }; } private void logGetConnectionStackTrace() { StringBuilder b = new StringBuilder(); - int i = 0; - synchronized (myLastStackTrace) { - for (Iterator iter = myLastStackTrace.descendingIterator(); iter.hasNext(); ) { - Exception nextStack = iter.next(); - b.append("\n\nPrevious request stack trace "); - b.append(i++); + ArrayList stackTraces = new ArrayList<>(myConnectionRequestStackTraces.values()); + + for (int i = 0; i < stackTraces.size(); i++) { + Exception nextStack = stackTraces.get(i); + b.append("\nPrevious request stack trace "); + b.append(i); + b.append(":"); + for (StackTraceElement next : nextStack.getStackTrace()) { + b.append("\n "); + b.append(next.getClassName()); + b.append("."); + b.append(next.getMethodName()); + b.append("("); + b.append(next.getFileName()); b.append(":"); - for (StackTraceElement next : nextStack.getStackTrace()) { - b.append("\n "); - b.append(next.getClassName()); - b.append("."); - b.append(next.getMethodName()); - b.append("("); - b.append(next.getFileName()); - b.append(":"); - b.append(next.getLineNumber()); - b.append(")"); - } + b.append(next.getLineNumber()); + b.append(")"); } + b.append("\n"); } ourLog.info(b.toString()); @@ -208,8 +228,8 @@ public class TestR4Config { @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java index f4d46f5d342..10ca0859722 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; @@ -50,7 +51,9 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import javax.sql.DataSource; import java.sql.Connection; +import java.util.Locale; import java.util.Properties; +import java.util.function.Predicate; import static org.junit.jupiter.api.Assertions.fail; @@ -68,6 +71,7 @@ import static org.junit.jupiter.api.Assertions.fail; public class TestR5Config { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR5Config.class); + public static final Predicate SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES = CircularQueueCaptureQueriesListener.DEFAULT_SELECT_INCLUSION_CRITERIA.and(t -> !t.toLowerCase(Locale.US).startsWith("select next value")); public static Integer ourMaxThreads; static { @@ -97,7 +101,8 @@ public class TestR5Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -173,8 +178,8 @@ public class TestR5Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR5"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java index 62480239d6a..7c8232108ca 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java @@ -24,9 +24,9 @@ import ca.uhn.fhir.jpa.subscription.SocketImplementation; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.ContainerProvider; +import jakarta.websocket.WebSocketContainer; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; @@ -34,15 +34,14 @@ import org.slf4j.LoggerFactory; import java.net.URI; import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +@ClientEndpoint public class WebsocketSubscriptionClient implements AfterEachCallback { private static final Logger ourLog = LoggerFactory.getLogger(WebsocketSubscriptionClient.class); private final Supplier myServerSupplier; private final Supplier myStorageSettings; - private WebSocketClient myWebSocketClient; + private jakarta.websocket.Session mySession; private SocketImplementation mySocketImplementation; /** @@ -64,20 +63,15 @@ public class WebsocketSubscriptionClient implements AfterEachCallback { RestfulServerExtension server = myServerSupplier.get(); assert server != null; - myWebSocketClient = new WebSocketClient(); mySocketImplementation = new SocketImplementation(theSubscriptionId, EncodingEnum.JSON); try { - myWebSocketClient.start(); URI echoUri = new URI("ws://localhost:" + server.getPort() + server.getWebsocketContextPath() + myStorageSettings.get().getWebsocketContextPath()); - ClientUpgradeRequest request = new ClientUpgradeRequest(); - ourLog.info("Connecting to : {}", echoUri); - Future connection; - connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); - Session session = connection.get(20, TimeUnit.SECONDS); - ourLog.info("Connected to WS: {}", session.isOpen()); + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + mySession = container.connectToServer(mySocketImplementation, echoUri); + ourLog.info("Connected to WS: {}", mySession.isOpen()); } catch (Exception e) { throw new InternalErrorException(e); } @@ -85,9 +79,9 @@ public class WebsocketSubscriptionClient implements AfterEachCallback { @Override public void afterEach(ExtensionContext theExtensionContext) throws Exception { - if (myWebSocketClient != null) { + if (mySession != null) { ourLog.info("Shutting down websocket client"); - myWebSocketClient.stop(); + mySession.close(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java index 8fb525d7509..26ae8841f28 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.config; -import javax.el.ExpressionFactory; +import jakarta.el.ExpressionFactory; import org.springframework.context.annotation.Configuration; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java index aa12360a223..7a283a495b1 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; -import javax.persistence.PersistenceException; +import jakarta.persistence.PersistenceException; import java.sql.SQLException; import static org.hamcrest.MatcherAssert.assertThat; @@ -57,7 +57,7 @@ public class HapiFhirHibernateJpaDialectTest { } outcome = mySvc.convertHibernateAccessException(new HibernateException("this is a message")); - assertThat(outcome.getMessage(), containsString("HibernateException: this is a message")); + assertThat(outcome.getMessage(), containsString("this is a message")); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java index 1d55666e468..6d14d780291 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java @@ -21,11 +21,11 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Root; +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; 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 2783ea3439e..e6b023dd6e0 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 @@ -8,15 +8,18 @@ import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.docker.RequiresDocker; import ca.uhn.fhir.util.VersionEnum; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.junit.jupiter.Testcontainers; import javax.sql.DataSource; import java.sql.SQLException; @@ -30,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +@RequiresDocker public class HapiSchemaMigrationTest { private static final Logger ourLog = LoggerFactory.getLogger(HapiSchemaMigrationTest.class); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java similarity index 97% rename from hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java rename to hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java index 7d88e567953..7fc5187295b 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java @@ -36,12 +36,12 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.fail; -@ContextConfiguration(classes = SchedulerServiceImplIT.TestConfiguration.class) +@ContextConfiguration(classes = SchedulerServiceImplTest.TestConfiguration.class) @ExtendWith(SpringExtension.class) @DirtiesContext -public class SchedulerServiceImplIT { +public class SchedulerServiceImplTest { - private static final Logger ourLog = LoggerFactory.getLogger(SchedulerServiceImplIT.class); + private static final Logger ourLog = LoggerFactory.getLogger(SchedulerServiceImplTest.class); public static final String SCHEDULED_JOB_ID = CountingJob.class.getName(); private static final AtomicInteger ourNameCounter = new AtomicInteger(); private static long ourTaskDelay; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java index af0b511a83e..3a77f72a620 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.search.builder.sql; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect; import ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; @@ -147,6 +148,6 @@ public class SearchQueryBuilderDialectMySqlTest extends BaseSearchQueryBuilderDi @Nonnull @Override protected Dialect createDialect() { - return new org.hibernate.dialect.MySQL57Dialect(); + return new HapiFhirMySQLDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java index 07eca1057f3..6e0c1933963 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java @@ -2,12 +2,13 @@ package ca.uhn.fhir.jpa.search.builder.sql; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; import ca.uhn.fhir.rest.param.DateParam; import com.healthmarketscience.sqlbuilder.Condition; import org.apache.commons.lang3.StringUtils; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.PostgreSQL10Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -45,7 +46,7 @@ public class SearchQueryBuilderDialectPostgresTest extends BaseSearchQueryBuilde logSql(generatedSql); String sql = generatedSql.getSql(); - assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_DATE t0 WHERE ((t0.HASH_IDENTITY = ?) AND ((t0.SP_VALUE_LOW_DATE_ORDINAL >= ?) AND (t0.SP_VALUE_HIGH_DATE_ORDINAL <= ?))) limit ?", sql); + assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_DATE t0 WHERE ((t0.HASH_IDENTITY = ?) AND ((t0.SP_VALUE_LOW_DATE_ORDINAL >= ?) AND (t0.SP_VALUE_HIGH_DATE_ORDINAL <= ?))) fetch first ? rows only", sql); assertEquals(4, StringUtils.countMatches(sql, "?")); assertEquals(4, generatedSql.getBindVariables().size()); @@ -58,6 +59,6 @@ public class SearchQueryBuilderDialectPostgresTest extends BaseSearchQueryBuilde @Nonnull @Override protected Dialect createDialect() { - return new PostgreSQL10Dialect(); + return new HapiFhirPostgresDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java index 2a6c042227b..f88e60cf7f6 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.search.builder.sql; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirSQLServerDialect; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; import org.apache.commons.lang3.StringUtils; import org.hibernate.dialect.Dialect; @@ -25,12 +26,19 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.endsWith("ORDER BY -t1.SP_VALUE_LOW DESC offset 0 rows fetch next ? rows only"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("ORDER BY -t1.SP_VALUE_LOW DESC offset 0 rows fetch first ? rows only"), sql); assertEquals(3, StringUtils.countMatches(sql, "?")); assertEquals(3, generatedSql.getBindVariables().size()); } + @Nonnull + private static String massageSql(String sql) { + sql = sql.replace("\n", " ").replaceAll(" +", " "); + return sql; + } + @Test public void testRangeWithOffset() { SearchQueryBuilder searchQueryBuilder = createSearchQueryBuilder(); @@ -40,7 +48,8 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.endsWith("select page0_ from query where __row__ >= ? and __row__ < ?"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("order by @@version offset ? rows fetch next ? rows only"), sql); assertEquals(3, StringUtils.countMatches(sql, "?")); assertEquals(3, generatedSql.getBindVariables().size()); @@ -55,7 +64,8 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.toUpperCase(Locale.ROOT).contains("SELECT TOP(?) T0.RES_ID FROM"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("order by @@version offset 0 rows fetch first ? rows only"), sql); assertEquals(2, StringUtils.countMatches(sql, "?")); assertEquals(2, generatedSql.getBindVariables().size()); @@ -64,6 +74,6 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild @Nonnull @Override protected Dialect createDialect() { - return new SQLServer2012Dialect(); + return new HapiFhirSQLServerDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java index 8821649c729..c0c1f0a8209 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java @@ -4,16 +4,16 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; import com.google.common.collect.Lists; -import org.hibernate.dialect.DerbyTenSevenDialect; -import org.hibernate.dialect.MariaDB103Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.MySQL8Dialect; -import org.hibernate.dialect.Oracle12cDialect; -import org.hibernate.dialect.PostgreSQL95Dialect; -import org.hibernate.dialect.SQLServer2005Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServer2012Dialect; +import org.hibernate.dialect.SQLServerDialect; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,7 +54,7 @@ public class SearchQueryBuilderTest { public void testRangeSqlServer2005_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2005Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -66,13 +66,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) ))", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY @@VERSION OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) order by @@version offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -80,7 +80,7 @@ public class SearchQueryBuilderTest { public void testRangeSqlServer2005_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2005Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -88,21 +88,18 @@ public class SearchQueryBuilderTest { // No range generated = builder.generate(null, null); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); // Max only generated = builder.generate(null, 10); -// assertEquals("SELECT TOP(?) t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY T0.RES_UPDATED ASC", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY T0.RES_UPDATED ASC OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); -// assertEquals("WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( SELECT TOP(?) t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC ) inner_query ) SELECT page0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?", generated.getSql()); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT top(?) t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(5, "Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -123,13 +120,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) ))", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY @@VERSION OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) order by @@version offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -145,19 +142,16 @@ public class SearchQueryBuilderTest { // No range generated = builder.generate(null, null); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); // Max only generated = builder.generate(null, 10); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC offset 0 rows fetch next ? rows only", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset 0 rows fetch next ? rows only", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset 0 rows fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); @@ -167,7 +161,7 @@ public class SearchQueryBuilderTest { public void testRangePostgreSQL95_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new PostgreSQL95Dialect()); + dialectProvider.setDialectForUnitTest(new PostgreSQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -179,13 +173,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ? offset ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 5, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -193,7 +187,7 @@ public class SearchQueryBuilderTest { public void testRangePostgreSQL95_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new PostgreSQL95Dialect()); + dialectProvider.setDialectForUnitTest(new PostgreSQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -206,13 +200,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST limit ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST limit ? offset ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 5, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -220,7 +214,7 @@ public class SearchQueryBuilderTest { public void testRangeOracle12c_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new Oracle12cDialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirOracleDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -232,13 +226,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("select * from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) where rownum <= ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("select * from ( select row_.*, rownum rownum_ from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) row_ where rownum <= ?) where rownum_ > ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 15, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -246,7 +240,7 @@ public class SearchQueryBuilderTest { public void testRangeOracle12c_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new Oracle12cDialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirOracleDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -259,13 +253,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("select * from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST ) where rownum <= ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("select * from ( select row_.*, rownum rownum_ from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST ) row_ where rownum <= ?) where rownum_ > ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 15, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -290,7 +284,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -320,7 +314,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); // assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -330,7 +324,7 @@ public class SearchQueryBuilderTest { public void testRangeMariaDB103_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MariaDB103Dialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirMariaDBDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -347,7 +341,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -356,7 +350,7 @@ public class SearchQueryBuilderTest { public void testRangeMariaDB103_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MariaDB103Dialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirMariaDBDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -377,7 +371,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); // assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -387,7 +381,7 @@ public class SearchQueryBuilderTest { public void testRangeDerbyTenSeven_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new DerbyTenSevenDialect()); + dialectProvider.setDialectForUnitTest(new DerbyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -399,13 +393,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first 10 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset 10 rows fetch next 5 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -413,7 +407,7 @@ public class SearchQueryBuilderTest { public void testRangeDerbyTenSeven_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new DerbyTenSevenDialect()); + dialectProvider.setDialectForUnitTest(new DerbyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -426,13 +420,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first 10 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset 10 rows fetch next 5 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java index dd4f999f059..8396aaab023 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java @@ -32,7 +32,7 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.hibernate.dialect.PostgreSQL10Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -52,8 +52,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ResourceUtils; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.Query; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -706,7 +706,7 @@ public class LoincFullLoadR4SandboxIT extends BaseJpaTest { @Override public String getHibernateDialect() { if (USE_REAL_DB) { - return PostgreSQL10Dialect.class.getName(); + return PostgreSQLDialect.class.getName(); } return super.getHibernateDialect(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 539f240c9a6..4acc1750f21 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml @@ -65,21 +65,6 @@ ${project.version} - - com.helger - ph-schematron - - - Saxon-HE - net.sf.saxon - - - - - com.helger - ph-commons - - org.springframework spring-web @@ -108,8 +93,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -119,48 +104,6 @@ h2 test - - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty.websocket - websocket-jetty-api - test - - - org.eclipse.jetty.websocket - websocket-core-client - test - - - org.eclipse.jetty.websocket - websocket-jetty-server - test - - org.apache.jena apache-jena-libs @@ -190,6 +133,13 @@ ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-test-utilities + test + ${project.version} + + diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 84e393ce4fd..58b7f4f9e4b 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -47,6 +47,9 @@ import ca.uhn.fhirtest.config.TestR4BConfig; import ca.uhn.fhirtest.config.TestR4Config; import ca.uhn.fhirtest.config.TestR5Config; import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.springframework.web.context.ContextLoaderListener; @@ -55,9 +58,6 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon import java.util.ArrayList; import java.util.List; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; public class TestRestfulServer extends RestfulServer { @@ -80,7 +80,7 @@ public class TestRestfulServer extends RestfulServer { public void destroy() { super.destroy(); ourLog.info("Server is shutting down"); - myAppCtx.destroy(); + myAppCtx.close(); } @SuppressWarnings("unchecked") diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java index 394e35eb70d..144aad7c2fb 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java @@ -3,7 +3,6 @@ package ca.uhn.fhirtest.config; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditContextServices; -import joptsimple.internal.Strings; import org.hl7.fhir.r4.model.Reference; import javax.annotation.Nonnull; @@ -73,6 +72,6 @@ public class FhirTestBalpAuditContextServices implements IBalpAuditContextServic parts[2] = "X"; parts[3] = "X"; } - return Strings.join(parts, "."); + return String.join(".", parts); } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java index b41af3fa429..d6c493cf78a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.to.FhirTesterMvcConfig; import ca.uhn.fhir.to.TesterConfig; import ca.uhn.fhirtest.mvc.SubscriptionPlaygroundController; -import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -116,7 +115,7 @@ public class FhirTesterConfig { return retVal; } - @Bean(autowire = Autowire.BY_TYPE) + @Bean public SubscriptionPlaygroundController subscriptionPlaygroundController() { return new SubscriptionPlaygroundController(); } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java index 03e838b3296..7d68437d1f5 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java @@ -12,6 +12,8 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; import ca.uhn.fhir.jpa.validation.ValidationSettings; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; @@ -28,8 +30,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -102,9 +102,11 @@ public class TestAuditConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaAudit"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index a3e4f203b2a..e19ac54a653 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -14,6 +14,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -34,7 +35,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -120,9 +120,11 @@ public class TestDstu2Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu2"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index 0e4e285b9a2..2531b87b4b8 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -35,7 +36,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -120,9 +120,11 @@ public class TestDstu3Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu3"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java index bafbdc2daf3..6cbb003cd29 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -36,7 +37,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -118,9 +118,11 @@ public class TestR4BConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index 1c62b7a5225..ba0a5db50af 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -21,6 +21,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -41,7 +42,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -121,9 +121,11 @@ public class TestR4Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java index e255f2627cb..775125e84fe 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -37,7 +38,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -130,9 +130,11 @@ public class TestR5Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR5"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java index 65043a50226..4ef6091af33 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.PreDestroy; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; @@ -26,7 +27,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.UUID; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java index 2405d91b39a..5a730df3671 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java @@ -3,9 +3,8 @@ package ca.uhn.fhirtest.joke; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java index 0de60267015..438eff48732 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java @@ -8,13 +8,13 @@ import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; import ca.uhn.fhir.util.VersionEnum; import ca.uhn.fhirtest.config.CommonConfig; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Properties; import java.util.Set; -import javax.annotation.PostConstruct; import javax.sql.DataSource; public class FhirTestAutoMigrator { diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java index f8dc38ef043..1b9a3f72e5f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java @@ -3,6 +3,7 @@ package ca.uhn.fhirtest.mvc; import ca.uhn.fhir.rest.client.impl.GenericClient; import ca.uhn.fhir.to.BaseController; import ca.uhn.fhir.to.model.HomeRequest; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Subscription; import org.springframework.ui.ModelMap; @@ -10,7 +11,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import java.util.ArrayList; import java.util.List; -import javax.servlet.http.HttpServletRequest; @org.springframework.stereotype.Controller() public class SubscriptionPlaygroundController extends BaseController { diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java index 9b5dd2c68f5..4c79a0ca6cc 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java @@ -3,8 +3,8 @@ package ca.uhn.fhirtest; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.webapp.WebAppContext; import org.hl7.fhir.dstu3.model.Organization; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Subscription; @@ -63,7 +63,7 @@ public class UhnFhirTestApp { root.setContextPath("/"); root.setDescriptor("hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml"); - root.setResourceBase("hapi-fhir-jpaserver-uhnfhirtest/target/hapi-fhir-jpaserver"); + root.setBaseResourceAsString("hapi-fhir-jpaserver-uhnfhirtest/target/hapi-fhir-jpaserver"); root.setParentLoaderPriority(true); diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 4ab4abd28ef..9e017d981b0 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java index a4d999a4ae5..d2832a79f56 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java @@ -20,6 +20,7 @@ package ca.uhn.hapi.fhir.cdshooks.api.json; import ca.uhn.fhir.model.api.IModelJson; +import com.fasterxml.jackson.annotation.JsonAnyGetter; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; @@ -29,6 +30,8 @@ import java.util.Map; import java.util.Set; public class CdsServiceRequestContextJson extends BaseCdsServiceJson implements IModelJson { + + @JsonAnyGetter private Map myMap; public String getString(String theKey) { diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java index 48e2fab4ab5..df6762d7151 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java @@ -28,6 +28,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.CdsServicePrefetch; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +40,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.PreDestroy; /** * This bean creates a customer-defined spring context which will diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java index 99c6f314c47..3155e16ea0f 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java @@ -35,13 +35,13 @@ import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Function; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; public class CdsServiceRegistryImpl implements ICdsServiceRegistry { private static final Logger ourLog = LoggerFactory.getLogger(CdsServiceRegistryImpl.class); diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 35f51c77d5b..0a008f0fe86 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 - 6.11.4-SNAPSHOT + 6.11.5-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 e5f03242298..67c643d07fe 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 @@ -28,11 +28,10 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; +import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - @Service public class MdmProviderLoader { @Autowired diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 5d58f58def4..8031d59df4d 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,12 +35,17 @@ io.swagger.core.v3 - swagger-models - 2.1.7 + swagger-models-jakarta io.swagger.core.v3 - swagger-core + swagger-core-jakarta + + + io.swagger.core.v3 + swagger-models + + org.webjars @@ -59,8 +64,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java index b49b65f39bd..1463ea3484a 100644 --- a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java +++ b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java @@ -56,6 +56,9 @@ import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; @@ -93,7 +96,7 @@ import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.TemplateResolution; import org.thymeleaf.templateresource.ClassLoaderTemplateResource; import org.thymeleaf.web.servlet.IServletWebExchange; -import org.thymeleaf.web.servlet.JavaxServletWebApplication; +import org.thymeleaf.web.servlet.JakartaServletWebApplication; import java.io.IOException; import java.io.InputStream; @@ -110,9 +113,6 @@ import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.rest.server.util.NarrativeUtil.sanitizeHtmlFragment; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; @@ -340,7 +340,7 @@ public class OpenApiInterceptor { HttpServletRequest servletRequest = theRequestDetails.getServletRequest(); ServletContext servletContext = servletRequest.getServletContext(); - JavaxServletWebApplication application = JavaxServletWebApplication.buildApplication(servletContext); + JakartaServletWebApplication application = JakartaServletWebApplication.buildApplication(servletContext); IServletWebExchange exchange = application.buildExchange(servletRequest, theResponse); WebContext context = new WebContext(exchange); context.setVariable(REQUEST_DETAILS, theRequestDetails); diff --git a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java index 441a656b826..5ba2667e959 100644 --- a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java +++ b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java @@ -5,7 +5,13 @@ import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -26,12 +32,20 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.hamcrest.Matchers; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseConformance; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.ActorDefinition; import org.hl7.fhir.r5.model.CapabilityStatement; import org.junit.jupiter.api.*; @@ -40,7 +54,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -51,7 +64,9 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class OpenApiInterceptorTest { diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index aaa176420d4..b3286ce44ec 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,8 +35,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java index 936fc86801d..fbca9e1add9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java @@ -30,7 +30,7 @@ import javax.annotation.Nullable; /** * Implementations of this interface represent a response back to the client from the server. It is - * conceptually similar to {@link javax.servlet.http.HttpServletResponse} but intended to be agnostic + * conceptually similar to {@link jakarta.servlet.http.HttpServletResponse} but intended to be agnostic * of the server framework being used. *

    * This class is a bit of an awkward abstraction given the two styles of servers it supports. diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java index a8a0ae4af20..0a9d904c1bb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.server.IRestfulServerDefaults; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -43,8 +45,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -325,7 +325,7 @@ public abstract class RequestDetails { * @throws UnsupportedEncodingException if the character set encoding used is not supported and the text cannot be decoded * @throws IllegalStateException if {@link #getInputStream} method has been called on this request * @throws IOException if an input or output exception occurred - * @see javax.servlet.http.HttpServletRequest#getInputStream + * @see jakarta.servlet.http.HttpServletRequest#getInputStream */ public abstract Reader getReader() throws IOException; @@ -397,7 +397,10 @@ public abstract class RequestDetails { /** * Returns the server base URL (with no trailing '/') for a given request + * + * @deprecated Use {@link #getFhirServerBase()} instead. Deprecated in HAPI FHIR 7.0.0 */ + @Deprecated public abstract String getServerBaseForRequest(); /** diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java index 4762a79e99c..3c769e0f915 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java @@ -31,8 +31,17 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Supplier; -import java.util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java index 561bc7dfb08..5f1952bdd3d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +30,6 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.util.Optional; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static java.util.Optional.ofNullable; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java index 5782290bdd2..6d192d30a1e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.Validate; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; - /** * Server address strategy which simply returns a hardcoded URL */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java index 96d682e1a0e..7fbdd99491c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java @@ -19,8 +19,8 @@ */ package ca.uhn.fhir.rest.server; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; /** * Provides the server base for a given incoming request. This can be used to account for diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java index f2b5eb31b9d..65ea9876d78 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.servlet.http.HttpServletRequest; - public interface IServerConformanceProvider { /** diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java index 4653bdbca74..5220bdef551 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; - /** * Determines the server's base using the incoming request */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java index febb8a729c9..f30a25e93c8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java @@ -33,8 +33,8 @@ public class ResourceBinding { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceBinding.class); - private String resourceName; - private LinkedList myMethodBindings = new LinkedList<>(); + private String myResourceName; + private final LinkedList myMethodBindings = new LinkedList<>(); /** * Constructor @@ -44,8 +44,8 @@ public class ResourceBinding { } public BaseMethodBinding getMethod(RequestDetails theRequest) { - if (null == myMethodBindings) { - ourLog.warn("No methods exist for resource: {}", resourceName); + if (myMethodBindings.isEmpty()) { + ourLog.warn("No methods exist for resource: {}", myResourceName); return null; } @@ -75,11 +75,11 @@ public class ResourceBinding { } public String getResourceName() { - return resourceName; + return myResourceName; } public void setResourceName(String resourceName) { - this.resourceName = resourceName; + this.myResourceName = resourceName; } public List getMethodBindings() { @@ -87,13 +87,20 @@ public class ResourceBinding { } public void addMethod(BaseMethodBinding method) { + if (myMethodBindings.stream() + .anyMatch( + t -> t.getMethod().toString().equals(method.getMethod().toString()))) { + ourLog.warn( + "The following method has been registered twice against this RestfulServer: {}", + method.getMethod()); + } this.myMethodBindings.push(method); } @Override public boolean equals(Object o) { if (!(o instanceof ResourceBinding)) return false; - return resourceName.equals(((ResourceBinding) o).getResourceName()); + return myResourceName.equals(((ResourceBinding) o).getResourceName()); } @Override diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 550a9d28fd3..30d5548e869 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -65,6 +65,11 @@ import ca.uhn.fhir.util.UrlPathTokenizer; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.VersionUtil; import com.google.common.collect.Lists; +import jakarta.servlet.ServletException; +import jakarta.servlet.UnavailableException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -99,11 +104,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.jar.Manifest; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.util.StringUtil.toUtf8String; import static java.util.stream.Collectors.toList; @@ -367,6 +367,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer t.close()); myGlobalBinding.getMethodBindings().forEach(t -> t.close()); myServerBinding.getMethodBindings().forEach(t -> t.close()); + + myResourceNameToBinding.clear(); + myGlobalBinding.getMethodBindings().clear(); + myServerBinding.getMethodBindings().clear(); } /** @@ -965,6 +969,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServerfalse to indicate that the server itself should not also provide a response. * @return Return true if processing should continue normally. This is generally the right thing to do. * If your interceptor is providing a response rather than letting HAPI handle the response normally, you diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java index a736b12725f..b4c8b559809 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java @@ -26,12 +26,12 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index 40c74261f2f..f58f02a37e9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -31,6 +31,9 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.text.StringSubstitutor; @@ -41,9 +44,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Date; import java.util.Map.Entry; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java index 69208893551..a734c691245 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java @@ -30,10 +30,10 @@ import ca.uhn.fhir.rest.server.method.ResourceParameter; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.nio.charset.Charset; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index 6ad6ec0716e..34a10d6f94a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -45,6 +45,9 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.text.StringEscapeUtils; @@ -66,9 +69,6 @@ import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java index 51df7c08ff8..00dfd8a6dbf 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java @@ -36,6 +36,8 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import java.util.HashMap; @@ -43,8 +45,6 @@ import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java index d1ceea5cab0..0fbbeb67d4b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -39,8 +41,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java index 7b000b05ab5..7dbcf69e73b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Enumeration; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * This interceptor creates verbose server log entries containing the complete request and response payloads. diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java index 776a6221778..affa0936f39 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java @@ -45,6 +45,8 @@ import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.bundle.ModifiableBundleEntry; import com.google.common.collect.ArrayListMultimap; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -62,8 +64,6 @@ import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * This interceptor can be used to automatically narrow the scope of searches in order to diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java index e9c7e56458c..5142e676d8b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java @@ -22,11 +22,17 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PreferHeader; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java index 19a43078ce3..fc90d4a2b01 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java @@ -19,9 +19,11 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.IResourceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java index 6233f6b6b0f..e3ad863f6e9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java @@ -44,6 +44,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; @@ -56,8 +58,6 @@ import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Date; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding { protected final ResponseBundleBuilder myResponseBundleBuilder; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java index 7d716075a87..9a19db59c55 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java @@ -33,6 +33,8 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -42,8 +44,6 @@ import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class GraphQLMethodBinding extends OperationMethodBinding { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index cdfa6b625d1..a66fe54607c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -27,15 +27,43 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.At; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Count; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.GraphQLQueryBody; +import ca.uhn.fhir.rest.annotation.GraphQLQueryUrl; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.IncludeParam; +import ca.uhn.fhir.rest.annotation.Offset; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.ServerBase; +import ca.uhn.fhir.rest.annotation.Since; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.annotation.TransactionParam; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.PatchTypeEnum; +import ca.uhn.fhir.rest.api.SearchContainedModeEnum; +import ca.uhn.fhir.rest.api.SearchTotalModeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.binder.CollectionBinder; import ca.uhn.fhir.rest.server.method.OperationParameter.IOperationParamConverter; import ca.uhn.fhir.rest.server.method.ResourceParameter.Mode; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -45,8 +73,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java index 29c0d40fe1f..7a4316d535e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java @@ -19,16 +19,59 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IQueryParameterAnd; +import ca.uhn.fhir.model.api.IQueryParameterOr; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.param.binder.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.param.CompositeAndListParam; +import ca.uhn.fhir.rest.param.CompositeOrListParam; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateAndListParam; +import ca.uhn.fhir.rest.param.DateOrListParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.HasAndListParam; +import ca.uhn.fhir.rest.param.HasOrListParam; +import ca.uhn.fhir.rest.param.HasParam; +import ca.uhn.fhir.rest.param.NumberAndListParam; +import ca.uhn.fhir.rest.param.NumberOrListParam; +import ca.uhn.fhir.rest.param.NumberParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.SpecialAndListParam; +import ca.uhn.fhir.rest.param.SpecialOrListParam; +import ca.uhn.fhir.rest.param.SpecialParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriAndListParam; +import ca.uhn.fhir.rest.param.UriOrListParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.param.binder.CalendarBinder; +import ca.uhn.fhir.rest.param.binder.DateBinder; +import ca.uhn.fhir.rest.param.binder.FhirPrimitiveBinder; +import ca.uhn.fhir.rest.param.binder.IParamBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterOrBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterTypeBinder; +import ca.uhn.fhir.rest.param.binder.StringBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.CollectionUtil; @@ -37,7 +80,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class SearchParameter extends BaseQueryParameter { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java index 0bd2c343c75..98cf3753987 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java @@ -19,11 +19,15 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.SortOrderEnum; +import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java index ec6f632cba4..00ed63c3457 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java @@ -29,7 +29,10 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java index d07a60d8366..1cbb83bebba 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java @@ -52,6 +52,8 @@ import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.collect.TreeMultimap; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.text.WordUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseConformance; @@ -75,8 +77,6 @@ import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index fead7ac698c..d8ee5d57e48 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; @@ -46,8 +48,6 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.zip.GZIPInputStream; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java index 7b991a17c7a..b51d6ca2960 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.server.servlet; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.BaseRestfulResponse; import ca.uhn.fhir.util.IoUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -35,8 +37,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.zip.GZIPOutputStream; import javax.annotation.Nonnull; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; public class ServletRestfulResponse extends BaseRestfulResponse { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java index e57ea6b157d..77effb67ab4 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java @@ -19,12 +19,13 @@ */ package ca.uhn.fhir.rest.server.servlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class ServletSubRequestDetails extends ServletRequestDetails { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java index 7692cd61767..9058a0cd0d9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.rest.server.util; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; - -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; /** * This interface isn't used by hapi-fhir-base, but is used by the diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java index d80f8dcf6f3..723aa57633b 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; public class IncomingRequestAddressStrategyTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java index a80f58d2bba..07d3f5e2e66 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SimpleBundleProviderTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java index fe377df7773..9694eccae74 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java @@ -5,7 +5,9 @@ import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Properties; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; class ConfigLoaderTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java index af3c956d1d8..2648b618be3 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java @@ -16,7 +16,7 @@ import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import static org.mockito.ArgumentMatchers.any; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java index a91acb56545..2cfa9aae844 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server.method; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MethodMatchEnumTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java index 799a7f77482..3eeb771b22f 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.rest.server.servlet; import ca.uhn.fhir.rest.api.Constants; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java index bb28d105053..bb6e6b97979 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java @@ -10,7 +10,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 2efd21502bb..153d21bec27 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 - 6.11.4-SNAPSHOT + 6.11.5-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 3db18cec3bb..577b77c3056 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 341ac8b5cfd..789346cd68a 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 - 6.11.4-SNAPSHOT + 6.11.5-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 ef1e667f1e8..26a01f302a4 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index b2ffa705596..830172b35a3 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.11.4-SNAPSHOT + 6.11.5-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 627cefbce72..11e06f6bf2b 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml @@ -60,8 +60,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api true diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java index 2e524810521..66f674ab2fb 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java @@ -45,6 +45,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; +import jakarta.persistence.EntityManagerFactory; +import jakarta.servlet.ServletException; import okhttp3.OkHttpClient; import org.apache.http.client.HttpClient; import org.springframework.beans.factory.ObjectProvider; @@ -75,8 +77,6 @@ import org.springframework.util.CollectionUtils; import java.util.List; import java.util.concurrent.ScheduledExecutorService; -import javax.persistence.EntityManagerFactory; -import javax.servlet.ServletException; import javax.sql.DataSource; /** 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 1c49210557f..f7e1fc2f252 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 - 6.11.4-SNAPSHOT + 6.11.5-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 e7e3ddb3c43..ed42f43c5bb 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 - 6.11.4-SNAPSHOT + 6.11.5-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 90d0122dc8c..14c6b0a2b61 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java index f5866d7e615..9c11a4ed57f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java @@ -50,8 +50,8 @@ public class PatientResourceProvider extends AbstractJaxRsResourceProvider ca.uhn.hapi.fhir hapi-fhir-spring-boot - 6.11.4-SNAPSHOT + 6.11.5-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 9c442072b80..e833cf5256a 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 4e9363295a7..6824520b126 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 53ddb291362..dbbbc4b1d7a 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -32,7 +32,7 @@ - org.hibernate + org.hibernate.orm hibernate-core diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java index 0c49ad0ab9b..1eadeca435b 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java @@ -84,26 +84,26 @@ public enum DriverTypeEnum { String retval; switch (this) { case H2_EMBEDDED: - retval = "hapifhirh2.sql"; + retval = "h2.sql"; break; case DERBY_EMBEDDED: - retval = "derbytenseven.sql"; + retval = "derby.sql"; break; case MYSQL_5_7: case MARIADB_10_1: - retval = "mysql57.sql"; + retval = "mysql.sql"; break; case POSTGRES_9_4: - retval = "hapifhirpostgres94-complete.sql"; + retval = "postgres.sql"; break; case ORACLE_12C: - retval = "oracle12c.sql"; + retval = "oracle.sql"; break; case MSSQL_2012: - retval = "sqlserver2012.sql"; + retval = "sqlserver.sql"; break; case COCKROACHDB_21_1: - retval = "cockroachdb201.sql"; + retval = "cockroachdb.sql"; break; default: throw new ConfigurationException( diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java index b9863105de6..551407f8e05 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java @@ -39,6 +39,7 @@ import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.service.ServiceRegistry; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.tool.schema.extract.spi.ExtractionContext; import org.hibernate.tool.schema.extract.spi.SequenceInformation; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -46,6 +47,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.LinkedCaseInsensitiveMap; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -230,6 +232,8 @@ public class JdbcUtils { return new ColumnType(ColumnTypeEnum.DOUBLE, length); case Types.FLOAT: return new ColumnType(ColumnTypeEnum.FLOAT, length); + case Types.TINYINT: + return new ColumnType(ColumnTypeEnum.TINYINT, length); default: throw new IllegalArgumentException(Msg.code(34) + "Don't know how to handle datatype " + dataType + " for column " + theColumnName + " on table " + theTableName); @@ -357,7 +361,7 @@ public class JdbcUtils { massageIdentifier(metadata, theTableName), null); - Set columnNames = new HashSet<>(); + LinkedCaseInsensitiveMap columnNames = new LinkedCaseInsensitiveMap<>(); while (indexes.next()) { String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); if (!theTableName.equalsIgnoreCase(tableName)) { @@ -366,10 +370,10 @@ public class JdbcUtils { String columnName = indexes.getString("COLUMN_NAME"); columnName = columnName.toUpperCase(Locale.US); - columnNames.add(columnName); + columnNames.put(columnName, columnName); } - return columnNames; + return columnNames.keySet(); } catch (SQLException e) { throw new InternalErrorException(Msg.code(38) + e); } @@ -388,7 +392,7 @@ public class JdbcUtils { new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData())); Set sequenceNames = new HashSet<>(); - if (dialect.supportsSequences()) { + if (dialect.getSequenceSupport().supportsSequences()) { // Use Hibernate to get a list of current sequences SequenceInformationExtractor sequenceInformationExtractor = @@ -412,6 +416,11 @@ public class JdbcUtils { return dialect; } + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return null; + } + @Override public ExtractedDatabaseMetaData getExtractedDatabaseMetaData() { return null; @@ -435,7 +444,7 @@ public class JdbcUtils { @Override public IdentifierHelper getIdentifierHelper() { return new NormalizingIdentifierHelperImpl( - this, null, true, true, true, null, null, null); + this, null, true, true, true, true, null, null, null); } @Override diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java index 7e9db2b7e3d..7ae686d268f 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java @@ -21,16 +21,16 @@ package ca.uhn.fhir.jpa.migrate.entity; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.util.VersionEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import org.hibernate.annotations.GenericGenerator; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; // Note even though we are using javax.persistence annotations here, we are managing these records outside of jpa // so these annotations are for informational purposes only diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java index d78223f7773..8c894e824c6 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java @@ -31,5 +31,11 @@ public enum ColumnTypeEnum { BLOB, CLOB, DOUBLE, - TEXT; + + /** + * Unlimited length text, with a column definition containing the annotation: + * @JdbcTypeCode(SqlTypes.LONG32VARCHAR) + */ + TEXT, + BIG_DECIMAL; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java index 03b222718cd..97ec7c8c673 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java @@ -123,13 +123,21 @@ public final class ColumnTypeToDriverTypeToSqlType { "oid"); // the PG driver will write oid into a `text` column setColumnType(ColumnTypeEnum.CLOB, DriverTypeEnum.MSSQL_2012, "varchar(MAX)"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.H2_EMBEDDED, "character varying"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.DERBY_EMBEDDED, "long varchar"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.H2_EMBEDDED, "character large object"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.DERBY_EMBEDDED, "clob"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MARIADB_10_1, "longtext"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MYSQL_5_7, "longtext"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "long"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "clob"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.POSTGRES_9_4, "text"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MSSQL_2012, "varchar(MAX)"); + + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.H2_EMBEDDED, "numeric(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.DERBY_EMBEDDED, "decimal(31,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MARIADB_10_1, "decimal(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MYSQL_5_7, "decimal(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.ORACLE_12C, "number(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.POSTGRES_9_4, "numeric(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MSSQL_2012, "numeric(38,2)"); } public static Map> getColumnTypeToDriverTypeToSqlType() { diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java index 77620832c74..f22a01fb5a5 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java @@ -93,7 +93,7 @@ public class AddColumnTest extends BaseTest { getMigrator().migrate(); fail(); } catch (HapiMigrationException e) { - assertThat(e.getMessage(), startsWith("HAPI-0047: Failure executing task \"Add column FOO_COLUMN on table FOO_TABLE\", aborting! Cause: ca.uhn.fhir.jpa.migrate.HapiMigrationException: HAPI-0061: Failed during task 4.0.0.2001.01: ")); + assertThat(e.getMessage(), startsWith("HAPI-0047: Failure executing task \"Add column foo_column on table FOO_TABLE\", aborting! Cause: ca.uhn.fhir.jpa.migrate.HapiMigrationException: HAPI-0061: Failed during task 4.0.0.2001.01: ")); } } diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 87c80e9aea2..6b5bfb0f4cc 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -26,8 +26,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided 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 f35052d3156..928c1f45f45 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 @@ -62,6 +62,7 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IIdType; @@ -83,7 +84,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters.ExportStyle; import static ca.uhn.fhir.util.DatatypeUtil.toStringValue; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java index 6e2396a6401..ecef4c12c05 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java @@ -38,6 +38,7 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IBase; @@ -57,7 +58,6 @@ import java.util.Date; import java.util.List; import java.util.Optional; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider.validatePreferAsyncHeader; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java index c0579dd167d..6f69dfe9eb6 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java @@ -24,6 +24,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; import org.slf4j.Logger; @@ -38,9 +41,6 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8_CTSUFFIX; import static ca.uhn.fhir.rest.api.Constants.CT_FHIR_NDJSON; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java index 22077e858ac..4a40f45b09f 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java @@ -22,15 +22,15 @@ package ca.uhn.fhir.batch2.jobs.imprt; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; /** * This class is the parameters model object for starting a diff --git a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java index 0b631445657..23fe01f5fb2 100644 --- a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java +++ b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java @@ -8,8 +8,8 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; public class BaseR4ServerTest { diff --git a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java index 7ba6807d180..69aea5aed76 100644 --- a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java +++ b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java @@ -279,7 +279,7 @@ public class BulkDataImportProviderTest { assertEquals(202, response.getStatusLine().getStatusCode()); assertEquals("Accepted", response.getStatusLine().getReasonPhrase()); assertEquals("120", response.getFirstHeader(Constants.HEADER_RETRY_AFTER).getValue()); - assertThat(response.getFirstHeader(Constants.HEADER_X_PROGRESS).getValue(), containsString("Job was created at 2022-01-01")); + assertThat(response.getFirstHeader(Constants.HEADER_X_PROGRESS).getValue(), containsString("Job was created at 2022-01")); } } diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index ab92cdf2bff..57f9d4347cf 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java index 9ae48d15300..9c6cd0832ca 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java @@ -24,7 +24,7 @@ import ca.uhn.fhir.model.api.annotation.PasswordField; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public class TestJobParameters implements IModelJson { diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 5329617d4fc..803455a67d2 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -20,6 +20,18 @@ + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + test + + ca.uhn.hapi.fhir hapi-fhir-base @@ -50,6 +62,7 @@ org.hibernate.validator hibernate-validator + org.glassfish jakarta.el @@ -72,11 +85,6 @@ spring-test test - - ch.qos.logback - logback-classic - test - org.springframework.data spring-data-commons diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java index a8935f5046a..adc1f4e9fe7 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java @@ -39,6 +39,8 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.springframework.data.domain.Page; @@ -53,8 +55,6 @@ import java.util.List; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java index 279d0d8d9e0..abf03a8a1df 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java @@ -26,16 +26,16 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java index 5504f50c4b2..9990b79070e 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.batch2.jobs.parameters; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Pattern; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.validation.constraints.Pattern; - public class PartitionedUrl implements IModelJson { @Override public String toString() { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java index 8a7aea8190b..a2777a2bcbd 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java @@ -345,8 +345,8 @@ public class JobDefinition { *

    * Validation: * Fields should be annotated with - * any appropriate javax.validation (JSR 380) annotations (e.g. - * {@link javax.validation.constraints.Min} or {@link javax.validation.constraints.Pattern}). + * any appropriate jakarta.validation (JSR 380) annotations (e.g. + * {@link jakarta.validation.constraints.Min} or {@link jakarta.validation.constraints.Pattern}). * In addition, if there are validation rules that are too complex to express using * JSR 380, you can also specify a programmatic validator using {@link #setParametersValidator(IJobParametersValidator)}. *

    @@ -357,7 +357,7 @@ public class JobDefinition { *

    * * @see ca.uhn.fhir.model.api.annotation.PasswordField - * @see javax.validation.constraints + * @see jakarta.validation.constraints * @see JobDefinition.Builder#setParametersValidator(IJobParametersValidator) */ @SuppressWarnings("unchecked") diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java index bea3a00065f..3bfb3b14ca3 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public class TestJobParameters implements IModelJson { diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java index 13104c12187..ba11ac13560 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java @@ -19,6 +19,7 @@ import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import ca.uhn.fhir.util.Logs; import ca.uhn.test.util.LogbackCaptureTestExtension; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -34,6 +35,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import java.util.Arrays; @@ -68,7 +70,7 @@ import static org.mockito.Mockito.when; public class JobMaintenanceServiceImplTest extends BaseBatch2Test { @RegisterExtension - LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) JobMaintenanceServiceImpl.ourLog, Level.WARN); + LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) LoggerFactory.getLogger("ca.uhn.fhir.log.batch_troubleshooting"), Level.WARN); @Mock IJobCompletionHandler myCompletionHandler; @Mock diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index aaa18119b78..c28054aa757 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -94,6 +94,11 @@ + + com.fasterxml.jackson.module + jackson-module-jakarta-xmlbind-annotations + + ca.uhn.hapi.fhir hapi-fhir-converter @@ -134,23 +139,16 @@ ${spring-security-core.version} - + + jakarta.servlet + jakarta.servlet-api + - These dependencies are added/swapped because the - Clinical Reasoning components no longer include it. - These will need to be adjusted / removed as soon as - the HAPI Jakarta PR hits --> - javax.servlet - javax.servlet-api + jakarta.xml.bind + jakarta.xml.bind-api - - javax.xml.bind - jaxb-api - 2.3.1 - - diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java index b88bb544a63..de10ff63f25 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java @@ -19,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Resource; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java index 1495d4ad4b5..a4449b3f0d1 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java @@ -26,8 +26,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Resource; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 2af9a4e6dbc..0a0a3ba68ca 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 - 6.11.4-SNAPSHOT + 6.11.5-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 1e5cb18bcfd..eac3ab35686 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 @@ -27,12 +27,11 @@ import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.interceptor.IMdmStorageInterceptor; import ca.uhn.fhir.mdm.interceptor.MdmSearchExpandingInterceptor; import ca.uhn.fhir.mdm.log.Logs; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu2.model.Subscription; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; - public class MdmSubmitterInterceptorLoader { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java index fed4b7d3615..3219c11152f 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java @@ -21,12 +21,12 @@ package ca.uhn.fhir.mdm.batch2.clear; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedJobParameters; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Pattern; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; import javax.annotation.Nonnull; -import javax.validation.constraints.Pattern; public class MdmClearJobParameters extends PartitionedJobParameters { @JsonProperty("resourceType") diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index 9a46e263b1e..4c7ab58eb6c 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index d5a2887d24f..f199f69626a 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -85,7 +85,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.hibernate.search @@ -132,8 +132,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java index dc05cb761ee..197f19e6572 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.subscription.SubscriptionConstants; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -47,8 +49,6 @@ import java.util.List; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; public abstract class BaseResourceCacheSynchronizer implements IResourceChangeListener { private static final Logger ourLog = LoggerFactory.getLogger(BaseResourceCacheSynchronizer.class); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java index 176983551fb..35a87a736d1 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java @@ -42,6 +42,7 @@ import ca.uhn.fhir.rest.param.HistorySearchDateRangeParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -54,7 +55,6 @@ import java.util.Map; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.servlet.http.HttpServletResponse; /** * Note that this interface is not considered a stable interface. While it is possible to build applications diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java index a269de9bec5..5fd5cadaadc 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java @@ -23,12 +23,11 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoComposition extends IFhirResourceDao { IBundleProvider getDocumentForComposition( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java index f97b47dacad..06b3d846a2c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java @@ -22,12 +22,11 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoEncounter extends IFhirResourceDao { IBundleProvider encounterInstanceEverything( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java index 82fbbf9135d..5c7049c59cd 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java @@ -22,10 +22,9 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.servlet.http.HttpServletResponse; - public interface IFhirResourceDaoObservation extends IFhirResourceDao { /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java index 1a14ce9ad5a..c4021290fee 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenOrListParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoPatient extends IFhirResourceDao { IBundleProvider patientInstanceEverything( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java index 52cce62c41a..6c776110764 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java @@ -42,6 +42,8 @@ import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -60,8 +62,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Optional; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java index a44f6cc5e19..51ac6b736b6 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.persistence.EntityManager; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collection; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; public interface ISearchBuilder> { String SEARCH_BUILDER_BEAN_NAME = "SearchBuilder"; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java index 0be025f0355..7d627254c08 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java @@ -23,7 +23,7 @@ import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.rest.api.server.RequestDetails; import java.util.function.Supplier; -import javax.validation.constraints.NotNull; +import javax.annotation.Nonnull; /** * Utility class wrapping a supplier in a transaction with the purpose of performing the supply operation with a @@ -33,13 +33,13 @@ public class PartitionAwareSupplier { private final HapiTransactionService myTransactionService; private final RequestDetails myRequestDetails; - @NotNull + @Nonnull public PartitionAwareSupplier(HapiTransactionService theTxService, RequestDetails theRequestDetails) { myTransactionService = theTxService; myRequestDetails = theRequestDetails; } - @NotNull + @Nonnull public T supplyInPartitionedContext(Supplier theResourcePersistentIdSupplier) { return myTransactionService.withRequest(myRequestDetails).execute(tx -> theResourcePersistentIdSupplier.get()); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index 348dce762be..ed9a68289d8 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -73,6 +73,7 @@ public class HapiTransactionService implements IHapiTransactionService { HapiTransactionService.class.getName() + "_EXISTING_SEARCH_PARAMS"; private static final Logger ourLog = LoggerFactory.getLogger(HapiTransactionService.class); private static final ThreadLocal ourRequestPartitionThreadLocal = new ThreadLocal<>(); + private static final ThreadLocal ourExistingTransaction = new ThreadLocal<>(); @Autowired protected IInterceptorBroadcaster myInterceptorBroadcaster; @@ -220,6 +221,11 @@ public class HapiTransactionService implements IHapiTransactionService { myTransactionManager = theTransactionManager; } + @VisibleForTesting + public void setPartitionSettingsForUnitTest(PartitionSettings thePartitionSettings) { + myPartitionSettings = thePartitionSettings; + } + @Nullable protected T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { final RequestPartitionId requestPartitionId; @@ -237,8 +243,9 @@ public class HapiTransactionService implements IHapiTransactionService { ourRequestPartitionThreadLocal.set(requestPartitionId); } - if (Objects.equals(previousRequestPartitionId, requestPartitionId)) { - if (canReuseExistingTransaction(theExecutionBuilder)) { + if (!myPartitionSettings.isPartitioningEnabled() + || Objects.equals(previousRequestPartitionId, requestPartitionId)) { + if (ourExistingTransaction.get() == this && canReuseExistingTransaction(theExecutionBuilder)) { /* * If we're already in an active transaction, and it's for the right partition, * and it's not a read-only transaction, we don't need to open a new transaction @@ -246,12 +253,22 @@ public class HapiTransactionService implements IHapiTransactionService { */ return executeInExistingTransaction(theCallback); } - } else if (myTransactionPropagationWhenChangingPartitions == Propagation.REQUIRES_NEW) { - return executeInNewTransactionForPartitionChange( - theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); } - return doExecuteInTransaction(theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + HapiTransactionService previousExistingTransaction = ourExistingTransaction.get(); + try { + ourExistingTransaction.set(this); + + if (myTransactionPropagationWhenChangingPartitions == Propagation.REQUIRES_NEW) { + return executeInNewTransactionForPartitionChange( + theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + } else { + return doExecuteInTransaction( + theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + } + } finally { + ourExistingTransaction.set(previousExistingTransaction); + } } @Nullable diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java index f199bbfe9be..558c7a4648d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java @@ -24,11 +24,11 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; public class SearchBuilderLoadIncludesParameters { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java index 7a017937c4e..03819a3dd79 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -39,7 +40,6 @@ import java.util.Date; import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; -import javax.servlet.http.HttpServletRequest; public abstract class BaseJpaProvider { public static final String REMOTE_ADDR = "req.remoteAddr"; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java index 8c2a8b35ff3..11b3dee3043 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java @@ -54,15 +54,14 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Required; import java.util.Date; -import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE; @@ -107,7 +106,6 @@ public abstract class BaseJpaResourceProvider extends B return myDao; } - @Required public void setDao(IFhirResourceDao theDao) { myDao = theDao; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java index 3ad030ff555..2a984c2f90d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java @@ -29,7 +29,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Required; public abstract class BaseStorageSystemProvider extends BaseJpaProvider { protected IFhirSystemDao myDao; @@ -74,7 +73,6 @@ public abstract class BaseStorageSystemProvider extends BaseJpaProvider { return myDao; } - @Required public void setDao(IFhirSystemDao theDao) { myDao = theDao; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java index 13bd3aede1f..f69a42a0df8 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.jpa.subscription.channel.api.IChannelSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; import ca.uhn.fhir.subscription.SubscriptionConstants; import ca.uhn.fhir.util.ThreadPoolUtil; +import jakarta.annotation.PreDestroy; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.annotation.Nonnull; -import javax.annotation.PreDestroy; public class LinkedBlockingChannelFactory implements IChannelFactory { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java index ed355cdc3fa..beb038ab49c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @@ -50,12 +51,17 @@ import javax.annotation.Nonnull; */ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListener { + public static final Predicate DEFAULT_SELECT_INCLUSION_CRITERIA = + t -> t.toLowerCase(Locale.US).startsWith("select"); private static final int CAPACITY = 1000; private static final Logger ourLog = LoggerFactory.getLogger(CircularQueueCaptureQueriesListener.class); private Queue myQueries; private AtomicInteger myCommitCounter; private AtomicInteger myRollbackCounter; + @Nonnull + private Predicate mySelectQueryInclusionCriteria = DEFAULT_SELECT_INCLUSION_CRITERIA; + /** * Constructor */ @@ -63,6 +69,16 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe startCollecting(); } + /** + * Sets an alternate inclusion criteria for select queries. This can be used to add + * additional criteria beyond the default value of {@link #DEFAULT_SELECT_INCLUSION_CRITERIA}. + */ + public CircularQueueCaptureQueriesListener setSelectQueryInclusionCriteria( + @Nonnull Predicate theSelectQueryInclusionCriteria) { + mySelectQueryInclusionCriteria = theSelectQueryInclusionCriteria; + return this; + } + @Override protected Queue provideQueryList() { return myQueries; @@ -133,6 +149,22 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe return getQueriesStartingWith(theStart, null); } + private List getQueriesMatching(Predicate thePredicate, String theThreadName) { + return getCapturedQueries().stream() + .filter(t -> theThreadName == null || t.getThreadName().equals(theThreadName)) + .filter(t -> thePredicate.test(t.getSql(false, false))) + .collect(Collectors.toList()); + } + + private List getQueriesMatching(Predicate thePredicate) { + return getQueriesMatching(thePredicate, null); + } + + private List getQueriesForCurrentThreadMatching(Predicate thePredicate) { + String threadName = Thread.currentThread().getName(); + return getQueriesMatching(thePredicate, threadName); + } + public int getCommitCount() { return myCommitCounter.get(); } @@ -145,7 +177,7 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Returns all SELECT queries executed on the current thread - Index 0 is oldest */ public List getSelectQueries() { - return getQueriesStartingWith("select"); + return getQueriesMatching(mySelectQueryInclusionCriteria); } /** @@ -173,7 +205,7 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Returns all SELECT queries executed on the current thread - Index 0 is oldest */ public List getSelectQueriesForCurrentThread() { - return getQueriesForCurrentThreadStartingWith("select"); + return getQueriesForCurrentThreadMatching(mySelectQueryInclusionCriteria); } /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java index 1717acdcbf7..efb4161dcca 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java @@ -23,6 +23,8 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.Validate; import org.hibernate.engine.jdbc.internal.BasicFormatterImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; @@ -31,6 +33,7 @@ import java.util.List; import static org.apache.commons.lang3.StringUtils.trim; public class SqlQuery { + private static final Logger ourLog = LoggerFactory.getLogger(SqlQuery.class); private final String myThreadName = Thread.currentThread().getName(); private final String mySql; private final List myParams; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java index 4a39746796c..d95da46a49e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.util.ThreadPoolUtil; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -35,7 +36,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.annotation.PreDestroy; /** * This implementation of the {@link IBalpAuditEventSink} transmits audit events to diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java index d7ae427a476..59855cc987a 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java @@ -36,9 +36,6 @@ import org.hl7.fhir.r4.model.AuditEvent; import java.nio.charset.StandardCharsets; import java.util.*; import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * The IHE Basic Audit Logging Pattern (BALP) interceptor can be used to autopmatically generate @@ -319,7 +316,7 @@ public class BalpAuditCaptureInterceptor { auditEvent.setOutcome(AuditEvent.AuditEventOutcome._0); auditEvent.setRecorded(new Date()); - auditEvent.getSource().getObserver().setDisplay(theRequestDetails.getServerBaseForRequest()); + auditEvent.getSource().getObserver().setDisplay(theRequestDetails.getFhirServerBase()); AuditEvent.AuditEventAgentComponent clientAgent = auditEvent.addAgent(); clientAgent.setWho(myContextServices.getAgentClientWho(theRequestDetails)); @@ -333,8 +330,8 @@ public class BalpAuditCaptureInterceptor { AuditEvent.AuditEventAgentComponent serverAgent = auditEvent.addAgent(); serverAgent.getType().addCoding(theProfile.getAgentServerTypeCoding()); - serverAgent.getWho().setDisplay(theRequestDetails.getServerBaseForRequest()); - serverAgent.getNetwork().setAddress(theRequestDetails.getServerBaseForRequest()); + serverAgent.getWho().setDisplay(theRequestDetails.getFhirServerBase()); + serverAgent.getNetwork().setAddress(theRequestDetails.getFhirServerBase()); serverAgent.setRequestor(false); AuditEvent.AuditEventAgentComponent userAgent = auditEvent.addAgent(); @@ -374,19 +371,14 @@ public class BalpAuditCaptureInterceptor { // Description StringBuilder description = new StringBuilder(); - HttpServletRequest servletRequest = theRequestDetails.getServletRequest(); - description.append(servletRequest.getMethod()); + description.append(theRequestDetails.getRequestType().name()); description.append(" "); - description.append(servletRequest.getRequestURI()); - if (isNotBlank(servletRequest.getQueryString())) { - description.append("?"); - description.append(servletRequest.getQueryString()); - } + description.append(theRequestDetails.getCompleteUrl()); queryEntity.setDescription(description.toString()); // Query String StringBuilder queryString = new StringBuilder(); - queryString.append(theRequestDetails.getServerBaseForRequest()); + queryString.append(theRequestDetails.getFhirServerBase()); queryString.append("/"); queryString.append(theRequestDetails.getRequestPath()); boolean first = true; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java index 412991d6d08..89003183c57 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java @@ -95,7 +95,7 @@ public interface IBalpAuditContextServices { @Nonnull RequestDetails theRequestDetails, @Nonnull IBaseResource theResource, @Nonnull IIdType theResourceId) { - String serverBaseUrl = theRequestDetails.getServerBaseForRequest(); + String serverBaseUrl = theRequestDetails.getFhirServerBase(); String resourceName = theResourceId.getResourceType(); return theResourceId.withServerBase(serverBaseUrl, resourceName).getValue(); } diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java index 141d3f72b89..3e324882f51 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java @@ -34,9 +34,14 @@ import org.mockito.junit.jupiter.MockitoExtension; import javax.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.stream.Collectors; import static ca.uhn.fhir.storage.interceptor.balp.BalpConstants.*; @@ -547,7 +552,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation?subject=Patient%2FP1"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1"); } @Test @@ -584,11 +589,11 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent); assertQuery(auditEvent, ourServer.getBaseUrl() + "/CodeSystem"); - assertQueryDescription(auditEvent, "GET /CodeSystem"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/CodeSystem"); } @Test - public void testSearch_ResponseIncludesSinglePatientCompartment_LoadPageTwo() { + public void testSearch_ResponseIncludesSinglePatientCompartment_LoadPageTwo() throws ExecutionException, InterruptedException { // Setup create10Observations("Patient/P1"); @@ -624,7 +629,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation?_count=5&subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation?subject=Patient%2FP1&_count=5"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1&_count=5"); auditEvent = myAuditEventCaptor.getAllValues().get(1); ourLog.info("Audit Event: {}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(auditEvent)); @@ -669,7 +674,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "POST /Observation/_search"); + assertQueryDescription(auditEvent, "POST " + ourServer.getBaseUrl() + "/Observation/_search"); } @Test @@ -704,7 +709,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation/_search?subject=Patient%2FP1"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); } @Test diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 58a42fc146f..32c2507e9ba 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -38,6 +38,13 @@ + + jakarta.servlet + jakarta.servlet-api + provided + true + + @@ -65,17 +72,6 @@ true
    - - - javax.servlet - servlet-api - 2.5 - provided - - commons-codec commons-codec @@ -97,36 +93,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -210,23 +176,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -284,7 +245,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java index 59f3614d32f..f58805ed7c6 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java @@ -36,22 +36,22 @@ import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.method.*; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2016may.model.Enumerations.ConformanceResourceStatus; -import org.hl7.fhir.dstu2016may.model.Enumerations.ResourceType; import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationKind; import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationParameterUse; import org.hl7.fhir.dstu2016may.model.*; import org.hl7.fhir.dstu2016may.model.Conformance.*; -import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationKind; +import org.hl7.fhir.dstu2016may.model.Enumerations.ResourceType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java deleted file mode 100644 index d8b81a61e16..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java +++ /dev/null @@ -1,172 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Binary; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class CreateBinaryDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static Binary ourLastBinary; - private static byte[] ourLastBinaryBytes; - private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; - - @BeforeEach - public void before() { - ourLastBinary = null; - ourLastBinaryBytes = null; - ourLastBinaryString = null; - } - - @Test - public void testRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); - post.addHeader("Content-Type", "application/foo"); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/foo", ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinaryBytes); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * Technically the client shouldn't be doing it this way, but we'll be accepting - */ - @Test - public void testRawBytesFhirContentType() throws Exception { - - Binary b = new Binary(); - b.setContentType("application/foo"); - b.setContent(new byte[] { 0, 1, 2, 3, 4 }); - String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new StringEntity(encoded)); - post.addHeader("Content-Type", Constants.CT_FHIR_JSON); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/foo", ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - } finally { - IOUtils.closeQuietly(status); - } - } - - @Test - public void testRawBytesFhirContentTypeContainingFhir() throws Exception { - - Patient p = new Patient(); - p.getText().setDivAsString("A PATIENT"); - - Binary b = new Binary(); - b.setContentType("application/xml+fhir"); - b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); - String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new StringEntity(encoded)); - post.addHeader("Content-Type", Constants.CT_FHIR_JSON); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/xml+fhir", ourLastBinary.getContentType()); - assertArrayEquals(b.getContent(), ourLastBinary.getContent()); - assertEquals(encoded, ourLastBinaryString); - assertArrayEquals(encoded.getBytes("UTF-8"), ourLastBinaryBytes); - } finally { - IOUtils.closeQuietly(status); - } - } - - @Test - public void testRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertNull(ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - } finally { - IOUtils.closeQuietly(status); - } - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - - public static class BinaryProvider implements IResourceProvider { - - @Create() - public MethodOutcome createBinary(@ResourceParam Binary theBinary, @ResourceParam String theBinaryString, @ResourceParam byte[] theBinaryBytes) { - ourLastBinary = theBinary; - ourLastBinaryString = theBinaryString; - ourLastBinaryBytes = theBinaryBytes; - return new MethodOutcome(new IdType("Binary/001/_history/002")); - } - - @Override - public Class getResourceType() { - return Binary.class; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java deleted file mode 100644 index 8b6f5cafa8f..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java +++ /dev/null @@ -1,283 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.OperationOutcomeIssueComponent; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class CreateDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static IBaseOperationOutcome ourReturnOo; - - @BeforeEach - public void before() { - ourReturnOo = null; - } - - /** - * #472 - */ - @Test - public void testCreateReturnsLocationHeader() throws Exception { - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse(Constants.CT_FHIR_JSON+"; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - - assertEquals(1, status.getHeaders("Location").length); - assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); - - } - - @Test - public void testCreateReturnsRepresentation() throws Exception { - ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse(Constants.CT_FHIR_JSON+"; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - String expectedContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"gender\":\"male\"}"; - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(expectedContent, responseContent); - - } - - /** - * #342 - */ - @Test - public void testCreateWithInvalidContent() throws Exception { - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - - assertThat(responseContent, containsString("", - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theIdParam) { - theIdParam.setId("1"); - return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo) - .setResource(theIdParam); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read() - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - @Search - public List search() { - ArrayList retVal = new ArrayList(); - - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(new IdType("Patient/0")); - p0.setDateExt(new DateType("2011-01-01")); - retVal.add(p0); - - Patient p1 = new Patient(); - p1.setId(new IdType("Patient/1")); - p1.addName().addFamily("The Family"); - retVal.add(p1); - - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java index bc39778fe31..6ba6d4cf304 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; @@ -11,46 +12,43 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2016may.model.IdType; import org.hl7.fhir.dstu2016may.model.OperationOutcome; import org.hl7.fhir.dstu2016may.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class CustomTypeServerDstu2_1 { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); + private static final FhirContext ourCtx = FhirContext.forDstu2_1(); private static String ourLastConditionalUrl; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu2_1.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastId = null; @@ -67,7 +65,7 @@ public class CustomTypeServerDstu2_1 { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -87,7 +85,7 @@ public class CustomTypeServerDstu2_1 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -100,7 +98,7 @@ public class CustomTypeServerDstu2_1 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals(Msg.code(365) + "Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } @Test @@ -109,7 +107,7 @@ public class CustomTypeServerDstu2_1 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -122,38 +120,15 @@ public class CustomTypeServerDstu2_1 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals(Msg.code(365) + "Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java deleted file mode 100644 index ad7fe63c5a3..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java +++ /dev/null @@ -1,132 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; -import ca.uhn.fhir.rest.annotation.Delete; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DeleteConditionalDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static IGenericClient ourHapiClient; - private static String ourLastConditionalUrl; - private static IdType ourLastIdParam; - private static boolean ourLastRequestWasDelete; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - - @BeforeEach - public void before() { - ourLastConditionalUrl = null; - ourLastIdParam = null; - ourLastRequestWasDelete = false; - } - - - - @Test - public void testSearchStillWorks() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("002"); - -// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); -// -// HttpResponse status = ourClient.execute(httpGet); -// -// String responseContent = IOUtils.toString(status.getEntity().getContent()); -// IOUtils.closeQuietly(status.getEntity().getContent()); -// -// ourLog.info("Response was:\n{}", responseContent); - - //@formatter:off - ourHapiClient - .delete() - .resourceConditionalByType(Patient.class) - .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS","SOMEID")) - .execute(); - //@formatter:on - - assertTrue(ourLastRequestWasDelete); - assertEquals(null, ourLastIdParam); - assertEquals("Patient?identifier=SOMESYS%7CSOMEID", ourLastConditionalUrl); - - } - - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - - public static class PatientProvider implements IResourceProvider { - - @Delete() - public MethodOutcome deletePatient(@IdParam IdType theIdParam, @ConditionalUrlParam String theConditional) { - ourLastRequestWasDelete = true; - ourLastConditionalUrl = theConditional; - ourLastIdParam = theIdParam; - return new MethodOutcome(new IdType("Patient/001/_history/002")); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java deleted file mode 100644 index e5c7478ec9d..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java +++ /dev/null @@ -1,255 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.annotation.ResourceDef; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class FormatParameterDstu2_1Test { - - private static final String VALUE_XML = ""; - private static final String VALUE_JSON = "{\"resourceType\":\"Patient\",\"id\":\"p1ReadId\",\"meta\":{\"profile\":[\"http://foo_profile\"]},\"identifier\":[{\"value\":\"p1ReadValue\"}]}"; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - - /** - * See #346 - */ - @Test - public void testFormatXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXmlFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml%2Bfhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXmlFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml+fhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJsonFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json%2Bfhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJsonFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json+fhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read(version = true) - public Patient read(@IdParam IdType theId) { - Patient p1 = new MyPatient(); - p1.setId("p1ReadId"); - p1.addIdentifier().setValue("p1ReadValue"); - return p1; - } - - } - - @ResourceDef(name = "Patient", profile = "http://foo_profile") - public static class MyPatient extends Patient { - - private static final long serialVersionUID = 1L; - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java deleted file mode 100644 index b2d8553b8f2..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java +++ /dev/null @@ -1,191 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.VersionUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@SuppressWarnings("deprecation") -public class MetadataConformanceDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu2_1Test.class); - - - @Test - public void testSummary() throws Exception { - String output; - - // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", "")); - assertThat(output, not(stringContainsInOrder("searchParam"))); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); - status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", ""))); - assertThat(output, stringContainsInOrder("searchParam")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testElements() throws Exception { - String output; - - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testHttpMethods() throws Exception { - String output; - - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(output, containsString(" getResourceType() { - return Patient.class; - } - - @Search - public List search(@OptionalParam(name="foo") StringParam theFoo) { - throw new UnsupportedOperationException(); - } - - @Validate() - public MethodOutcome validate(@ResourceParam Patient theResource) { - return new MethodOutcome(); - } - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java deleted file mode 100644 index 2075b04e46e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java +++ /dev/null @@ -1,786 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Bundle; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent; -import org.hl7.fhir.dstu2016may.model.DateTimeType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.IntegerType; -import org.hl7.fhir.dstu2016may.model.Money; -import org.hl7.fhir.dstu2016may.model.OperationDefinition; -import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationParameterUse; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.hl7.fhir.dstu2016may.model.UnsignedIntType; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInRelativeOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class OperationServerDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - - private static IdType ourLastId; - private static String ourLastMethod; - private static StringType ourLastParam1; - private static Patient ourLastParam2; - private static List ourLastParam3; - private static Money ourLastParamMoney1; - private static UnsignedIntType ourLastParamUnsignedInt1; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private IGenericClient myFhirClient; - - @BeforeEach - public void before() { - ourLastParam1 = null; - ourLastParam2 = null; - ourLastParam3 = null; - ourLastParamUnsignedInt1 = null; - ourLastParamMoney1 = null; - ourLastId = null; - ourLastMethod = ""; - - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - } - - - @Test - public void testConformance() { - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLogResponseBody(true); - myFhirClient.registerInterceptor(loggingInterceptor); - - Conformance p = myFhirClient.fetchConformance().ofType(Conformance.class).prettyPrint().execute(); - ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p)); - - List ops = p.getRest().get(0).getOperation(); - assertThat(ops.size(), greaterThan(1)); - - List opNames = toOpNames(ops); - assertThat(opNames, containsInRelativeOrder("OP_TYPE")); - -// OperationDefinition def = (OperationDefinition) ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getResource(); - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId(ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getReferenceElement()).execute(); - assertEquals("OP_TYPE", def.getCode()); - } - - /** - * See #380 - */ - @Test - public void testOperationDefinition() { - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); - - ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def)); - -// @OperationParam(name="PARAM1") StringType theParam1, -// @OperationParam(name="PARAM2") Patient theParam2, -// @OperationParam(name="PARAM3", min=2, max=5) List theParam3, -// @OperationParam(name="PARAM4", min=1) List theParam4, - - assertEquals(4, def.getParameter().size()); - assertEquals("PARAM1", def.getParameter().get(0).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(0).getUse()); - assertEquals(0, def.getParameter().get(0).getMin()); - assertEquals("1", def.getParameter().get(0).getMax()); - - assertEquals("PARAM2", def.getParameter().get(1).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(1).getUse()); - assertEquals(0, def.getParameter().get(1).getMin()); - assertEquals("1", def.getParameter().get(1).getMax()); - - assertEquals("PARAM3", def.getParameter().get(2).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(2).getUse()); - assertEquals(2, def.getParameter().get(2).getMin()); - assertEquals("5", def.getParameter().get(2).getMax()); - - assertEquals("PARAM4", def.getParameter().get(3).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(3).getUse()); - assertEquals(1, def.getParameter().get(3).getMin()); - assertEquals("*", def.getParameter().get(3).getMax()); - - } - - private List toOpNames(List theOps) { - ArrayList retVal = new ArrayList(); - for (ConformanceRestOperationComponent next : theOps) { - retVal.add(next.getName()); - } - return retVal; - } - - @Test - public void testInstanceEverythingGet() throws Exception { - - // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); - CloseableHttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals("instance $everything", ourLastMethod); - assertThat(response, startsWith(" getResourceType() { - return Patient.class; - } - - //@formatter:off - @Operation(name="$OP_INSTANCE") - public Parameters opInstance( - @IdParam IdType theId, - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_INSTANCE"; - ourLastId = theId; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_INSTANCE_OR_TYPE") - public Parameters opInstanceOrType( - @IdParam(optional=true) IdType theId, - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_INSTANCE_OR_TYPE"; - ourLastId = theId; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_PROFILE_DT2", idempotent=true) - public Bundle opProfileType( - @OperationParam(name="PARAM1") Money theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_PROFILE_DT2"; - ourLastParamMoney1 = theParam1; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_PROFILE_DT", idempotent=true) - public Bundle opProfileType( - @OperationParam(name="PARAM1") UnsignedIntType theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_PROFILE_DT"; - ourLastParamUnsignedInt1 = theParam1; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - //@formatter:off - @SuppressWarnings("unused") - @Operation(name="$OP_TYPE", idempotent=true) - public Parameters opType( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2, - @OperationParam(name="PARAM3", min=2, max=5) List theParam3, - @OperationParam(name="PARAM4", min=1) List theParam4 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_TYPE_ONLY_STRING", idempotent=true) - public Parameters opTypeOnlyString( - @OperationParam(name="PARAM1") StringType theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE"; - ourLastParam1 = theParam1; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_TYPE_RET_BUNDLE") - public Bundle opTypeRetBundle( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE_RET_BUNDLE"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - @Operation(name = "$everything", idempotent=true) - public Bundle patientEverything(@IdParam IdType thePatientId) { - ourLastMethod = "instance $everything"; - ourLastId = thePatientId; - return new Bundle(); - } - - /** - * Just to make sure this method doesn't "steal" calls - */ - @Read - public Patient read(@IdParam IdType theId) { - ourLastMethod = "read"; - Patient retVal = new Patient(); - retVal.setId(theId); - return retVal; - } - - } - - public static class PlainProvider { - - //@formatter:off - @Operation(name="$OP_INSTANCE_BUNDLE_PROVIDER", idempotent=true) - public IBundleProvider opInstanceReturnsBundleProvider() { - ourLastMethod = "$OP_INSTANCE_BUNDLE_PROVIDER"; - - List resources = new ArrayList<>(); - for (int i =0; i < 100;i++) { - Patient p = new Patient(); - p.setId("Patient/" + i); - p.addName().addFamily("Patient " + i); - resources.add(p); - } - - return new SimpleBundleProvider(resources); - } - - //@formatter:off - @Operation(name="$OP_SERVER") - public Parameters opServer( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_SERVER"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_SERVER_LIST_PARAM") - public Parameters opServerListParam( - @OperationParam(name="PARAM2") Patient theParam2, - @OperationParam(name="PARAM3") List theParam3 - ) { - //@formatter:on - - ourLastMethod = "$OP_SERVER_LIST_PARAM"; - ourLastParam2 = theParam2; - ourLastParam3 = theParam3; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java deleted file mode 100644 index d8054c0c07e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java +++ /dev/null @@ -1,514 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.param.TokenParamModifier; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.UrlUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationDefinition; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class OperationServerWithSearchParamTypesDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - - private static String ourLastMethod; - private static List ourLastParamValStr; - private static List ourLastParamValTok; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - @BeforeEach - public void before() { - ourLastMethod = ""; - ourLastParamValStr = null; - ourLastParamValTok = null; - } - - private HttpServletRequest createHttpServletRequest() { - HttpServletRequest req = mock(HttpServletRequest.class); - when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search"); - when(req.getServletPath()).thenReturn("/fhir"); - when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search")); - when(req.getContextPath()).thenReturn("/FhirStorm"); - return req; - } - - private ServletConfig createServletConfig() { - ServletConfig sc = mock(ServletConfig.class); - when(sc.getServletContext()).thenReturn(null); - return sc; - } - - @Test - public void testAndListWithParameters() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR1A,VALSTR1B")); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR2A,VALSTR2B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK1A|VALTOK1B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - } - - @Test - public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testGenerateConformance() throws Exception { - RestfulServer rs = new RestfulServer(ourCtx); - rs.setProviders(new PatientProvider()); - - ServerConformanceProvider sc = new ServerConformanceProvider(rs); - rs.setServerConformanceProvider(sc); - - rs.init(createServletConfig()); - - Conformance conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); - - String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); - ourLog.info(conf); - //@formatter:off - assertThat(conf, stringContainsInOrder( - "", - "", - "", - "" - )); - assertThat(conf, stringContainsInOrder( - "", - "", - "" - )); - assertThat(conf, stringContainsInOrder( - "", - "", - "" - )); - //@formatter:on - - /* - * Check the operation definitions themselves - */ - OperationDefinition andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-andlist"), createRequestDetails(rs)); - String def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-andlist-withnomax"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - OperationDefinition orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-orlist"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-orlist-withnomax"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - } - - @Test - public void testNonRepeatingWithParams() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOKA|VALTOKB")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $nonrepeating", ourLastMethod); - } - @Test - public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $nonrepeating", ourLastMethod); - } - - @Test - public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertTrue(ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).isExact()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(TokenParamModifier.NOT, ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getModifier()); - assertEquals("type $nonrepeating", ourLastMethod); - } - - @Test - public void testOrListWithParameters() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR1A,VALSTR1B")); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR2A,VALSTR2B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK1A|VALTOK1B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2_1(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - - @Operation(name = "$andlist", idempotent = true) - public Parameters andlist( - //@formatter:off - @OperationParam(name="valstr", max=10) StringAndListParam theValStr, - @OperationParam(name="valtok", max=10) TokenAndListParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr.getValuesAsQueryTokens(); - ourLastParamValTok = theValTok.getValuesAsQueryTokens(); - - return createEmptyParams(); - } - - @Operation(name = "$andlist-withnomax", idempotent = true) - public Parameters andlistWithNoMax( - //@formatter:off - @OperationParam(name="valstr") StringAndListParam theValStr, - @OperationParam(name="valtok") TokenAndListParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr.getValuesAsQueryTokens(); - ourLastParamValTok = theValTok.getValuesAsQueryTokens(); - - return createEmptyParams(); - } - - /** - * Just so we have something to return - */ - private Parameters createEmptyParams() { - Parameters retVal = new Parameters(); - retVal.setId("100"); - return retVal; - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Operation(name = "$nonrepeating", idempotent = true) - public Parameters nonrepeating( - //@formatter:off - @OperationParam(name="valstr") StringParam theValStr, - @OperationParam(name="valtok") TokenParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $nonrepeating"; - ourLastParamValStr = Collections.singletonList(new StringOrListParam().add(theValStr)); - ourLastParamValTok = Collections.singletonList(new TokenOrListParam().add(theValTok)); - - return createEmptyParams(); - } - - @Operation(name = "$orlist", idempotent = true) - public Parameters orlist( - //@formatter:off - @OperationParam(name="valstr", max=10) List theValStr, - @OperationParam(name="valtok", max=10) List theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr; - ourLastParamValTok = theValTok; - - return createEmptyParams(); - } - - @Operation(name = "$orlist-withnomax", idempotent = true) - public Parameters orlistWithNoMax( - //@formatter:off - @OperationParam(name="valstr") List theValStr, - @OperationParam(name="valtok") List theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr; - ourLastParamValTok = theValTok; - - return createEmptyParams(); - } - - } - - - private RequestDetails createRequestDetails(RestfulServer theServer) { - ServletRequestDetails retVal = new ServletRequestDetails(); - retVal.setServer(theServer); - return retVal; - } - -} - diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java deleted file mode 100644 index 970b61397e7..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java +++ /dev/null @@ -1,188 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Patch; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.PatchTypeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class PatchDstu2_1Test { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchDstu2_1Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static PatchTypeEnum ourLastPatchType; - private static String ourLastBody; - private static IdType ourLastId; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastBody = null; - ourLastId = null; - } - - @Test - public void testPatchValidJson() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); - httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - try (CloseableHttpResponse status = ourClient.execute(httpPatch)) { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("
    OK
    ", responseContent); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - assertEquals(PatchTypeEnum.JSON_PATCH, ourLastPatchType); - } - - @Test - public void testPatchValidXml() throws Exception { - String requestContents = ""; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_XML_PATCH))); - - try (CloseableHttpResponse status = ourClient.execute(httpPatch)) { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("
    OK
    ", responseContent); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - assertEquals(PatchTypeEnum.XML_PATCH, ourLastPatchType); - } - - @Test - public void testPatchValidJsonWithCharset() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX))); - CloseableHttpResponse status = ourClient.execute(httpPatch); - - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - } - - @Test - public void testPatchInvalidMimeType() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse("text/plain; charset=UTF-8"))); - CloseableHttpResponse status = ourClient.execute(httpPatch); - - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(400, status.getStatusLine().getStatusCode()); - assertEquals("", responseContent); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Patch - public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) { - ourLastMethod = "patientPatch"; - ourLastBody = theBody; - ourLastId = theId; - ourLastPatchType = thePatchType; - OperationOutcome retVal = new OperationOutcome(); - retVal.getText().setDivAsString("
    OK
    "); - return retVal; - } - //@formatter:on - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java deleted file mode 100644 index ef1892e907c..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java +++ /dev/null @@ -1,122 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ReadDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - - @Test - public void testRead() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); - assertEquals("http://localhost:" + ourPort + "/Patient/2/_history/2", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read(version=true) - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - if (theIdParam.hasVersionIdPart() == false) { - p0.setIdElement(p0.getIdElement().withVersion("2")); - } - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java deleted file mode 100644 index 126f01451ae..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java +++ /dev/null @@ -1,184 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Count; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchCountParamDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static Integer ourLastParam; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastParam = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - assertEquals(new Integer(2), ourLastParam); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #372 - */ - @Test - public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("searchWithNoCountParam", ourLastMethod); - assertEquals(null, ourLastParam); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier, - @Count() Integer theParam - ) { - ourLastMethod = "search"; - ourLastParam = theParam; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search(queryName="searchWithNoCountParam") - public List searchWithNoCountParam() { - ourLastMethod = "searchWithNoCountParam"; - ourLastParam = null; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java deleted file mode 100644 index dea3760cf4e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java +++ /dev/null @@ -1,142 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static TokenAndListParam ourIdentifiers; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourIdentifiers = null; - } - - @Test - public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - - assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testSearchWithInvalidChain() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(400, status.getStatusLine().getStatusCode()); - - OperationOutcome oo = (OperationOutcome) ourCtx.newXmlParser().parseResource(responseContent); - assertEquals(Msg.code(1935) + "Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)", oo.getIssue().get(0).getDiagnostics()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers - ) { - ourLastMethod = "search"; - ourIdentifiers = theIdentifiers; - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java deleted file mode 100644 index fcd300cee41..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java +++ /dev/null @@ -1,121 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.HasAndListParam; -import ca.uhn.fhir.rest.param.HasParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchHasParamDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static HasAndListParam ourLastParam; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastParam = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - - HasParam param = ourLastParam.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0); - assertEquals("Encounter", param.getTargetResourceType()); - assertEquals("patient", param.getReferenceFieldName()); - assertEquals("type", param.getParameterName()); - assertEquals("SURG", param.getParameterValue()); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier, - @OptionalParam(name="_has") HasAndListParam theParam - ) { - ourLastMethod = "search"; - ourLastParam = theParam; - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java deleted file mode 100644 index 7ab78bee8b9..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java +++ /dev/null @@ -1,260 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import com.google.common.collect.Lists; -import org.apache.commons.io.IOUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicNameValuePair; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchPostDstu2_1Test { - - public class ParamLoggingInterceptor extends InterceptorAdapter { - - @Override - public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { - ourLog.info("Params: {}", theRequest.getParameterMap()); - return true; - } - - - } - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static SortSpec ourLastSortSpec; - private static StringAndListParam ourLastName; - private static RestfulServer ourServlet; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastSortSpec = null; - ourLastName = null; - - ourServlet.getInterceptorService().unregisterAllInterceptors(); - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsNoInterceptorsYesParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format="+Constants.CT_FHIR_JSON); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_JSON, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsNoInterceptorsNoParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_XML, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsYesInterceptorsYesParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format="+Constants.CT_FHIR_JSON); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_JSON, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsYesInterceptorsNoParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_XML, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @Sort SortSpec theSortSpec, - @OptionalParam(name=Patient.SP_NAME) StringAndListParam theName - ) { - ourLastMethod = "search"; - ourLastSortSpec = theSortSpec; - ourLastName = theName; - ArrayList retVal = new ArrayList<>(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("foo")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java deleted file mode 100644 index 2abec3a4fb3..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java +++ /dev/null @@ -1,132 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.SortOrderEnum; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchSortDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static SortSpec ourLastSortSpec; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastSortSpec = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - - assertEquals("param1", ourLastSortSpec.getParamName()); - assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getOrder()); - - assertEquals("param2", ourLastSortSpec.getChain().getParamName()); - assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getOrder()); - - assertEquals("param3", ourLastSortSpec.getChain().getChain().getParamName()); - assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getChain().getChain().getOrder()); - - assertEquals("param4", ourLastSortSpec.getChain().getChain().getChain().getParamName()); - assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getChain().getChain().getOrder()); - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @Sort SortSpec theSortSpec - ) { - ourLastMethod = "search"; - ourLastSortSpec = theSortSpec; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java index e565e08a5f0..3f80fe2b250 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2016may.model.HumanName; import org.hl7.fhir.dstu2016may.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,13 +28,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); + private static final FhirContext ourCtx = FhirContext.forDstu2_1(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +50,7 @@ public class SearchWithGenericListDstu2_1Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,43 +58,17 @@ public class SearchWithGenericListDstu2_1Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -109,7 +82,7 @@ public class SearchWithGenericListDstu2_1Test { public List searchByIdentifier( @RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) { ourLastMethod = "searchByIdentifier"; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); return retVal; } diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java deleted file mode 100644 index 97b88e3bcad..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java +++ /dev/null @@ -1,135 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.rest.annotation.IncludeParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Organization; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchWithIncludesDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @Test - public void testSearchIncludesReferences() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - // Response should include both the patient, and the organization that was referred to - // by a linked resource - - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Search() - public List search(@IncludeParam() Set theIncludes) { - ourLog.info("Includes: {}", theIncludes); - - // Grandparent has ID, other orgs don't - Organization gp = new Organization(); - gp.setId("Organization/GP"); - gp.setName("grandparent"); - - Organization parent = new Organization(); - parent.setName("parent"); - parent.getPartOf().setResource(gp); - - Organization child = new Organization(); - child.setName("child"); - child.getPartOf().setResource(parent); - - Patient patient = new Patient(); - patient.setId("Patient/FOO"); - patient.getManagingOrganization().setResource(child); - - ArrayList retVal = new ArrayList(); - retVal.add(patient); - return retVal; - } - //@formatter:on - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java deleted file mode 100644 index d486934fa09..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java +++ /dev/null @@ -1,172 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchWithServerAddressStrategyDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - - @Test - public void testIncomingRequestAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - } - - @Test - public void testApacheProxyAddressStrategy() throws Exception { - - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - httpGet.addHeader("x-forwarded-proto", "https"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - } - - @Test - public void testHardcodedAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Search() - public List searchByIdentifier() { - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("1")); - retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("2")); - return retVal; - } - //@formatter:on - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java deleted file mode 100644 index ec0f45e8320..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java +++ /dev/null @@ -1,137 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ServerExceptionDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static BaseServerResponseException ourException; - - @Test - public void testAddHeadersNotFound() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - - ourException = new ResourceNotFoundException("SOME MESSAGE"); - ourException.addResponseHeader("X-Foo", "BAR BAR"); - - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - - assertEquals(404, status.getStatusLine().getStatusCode()); - assertEquals("BAR BAR", status.getFirstHeader("X-Foo").getValue()); - assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("HAPI FHIR")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testAuthorize() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - - ourException = new AuthenticationException().addAuthenticateHeaderForRealm("REALM"); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - - assertEquals(401, status.getStatusLine().getStatusCode()); - assertEquals("Basic realm=\"REALM\"", status.getFirstHeader("WWW-Authenticate").getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Search() - public List search() { - throw ourException; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java deleted file mode 100644 index 08854c9b191..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java +++ /dev/null @@ -1,422 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpTrace; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.CodeType; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ServerMimetypeDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @Test - public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(content); - Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class, content); - List strings = toStrings(conf.getFormat()); - assertThat(strings, contains(Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON)); - } finally { - status.close(); - } - } - - - - private List toStrings(List theFormat) { - ArrayList retVal = new ArrayList<>(); - for (CodeType next : theFormat) { - retVal.add(next.asStringValue()); - } - return retVal; - } - - - - @Test - public void testCreateWithXmlLegacyNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(req); - try { - ourLog.info(status.toString()); - assertEquals(400, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testHttpTrackNotEnabled() throws Exception { - HttpRequestBase req = new HttpRequestBase() { - @Override - public String getMethod() { - return "TRACK"; - }}; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); - - CloseableHttpResponse status = ourClient.execute(req); - try { - ourLog.info(status.toString()); - assertEquals(400, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testCreateWithXmlNewNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML+ "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithXmlNewWithAcceptHeaderReturnsOperationOutcome() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML); - httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonLegacyNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":[\"FAMILY\"]}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonNewNoAcceptHeaderReturnsOperationOutcome() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON+ "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonNewWithAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":[\"FAMILY\"]}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testSearchWithFormatXmlSimple() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatXmlLegacy() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatXmlNew() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - - - @Test - public void testSearchWithFormatJsonSimple() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatJsonLegacy() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatJsonNew() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theIdParam) { - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics(theIdParam.getName().get(0).getFamilyAsSingleString()); - theIdParam.setId("1"); - theIdParam.getMeta().setVersionId("1"); - return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(oo).setResource(theIdParam); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read() - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - @Search - public List search() { - ArrayList retVal = new ArrayList<>(); - - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(new IdType("Patient/0")); - p0.setDateExt(new DateType("2011-01-01")); - retVal.add(p0); - - Patient p1 = new Patient(); - p1.setId(new IdType("Patient/1")); - p1.addName().addFamily("The Family"); - retVal.add(p1); - - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java index afe0e9b97e0..7a4c5855cee 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java @@ -14,7 +14,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java deleted file mode 100644 index 20b571725b5..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java +++ /dev/null @@ -1,108 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class UnclassifiedServerExceptionDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static UnclassifiedServerFailureException ourException; - - @Test - public void testSearch() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - ourException = new UnclassifiedServerFailureException(477, "SOME MESSAGE", operationOutcome); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - assertEquals(477, status.getStatusLine().getStatusCode()); - //assertEquals("SOME MESSAGE", status.getStatusLine().getReasonPhrase()); - assertThat(responseContent, stringContainsInOrder("business-rule")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Search() - public List search() { - throw ourException; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java deleted file mode 100644 index 6dbba1ac3d9..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java +++ /dev/null @@ -1,195 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Update; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class UpdateDstu2_1Test { - private static CloseableHttpClient ourClient; - private static String ourConditionalUrl; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static IdType ourId; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @BeforeEach - public void before() { - ourConditionalUrl = null; - ourId = null; - } - - @Test - public void testUpdateConditional() throws Exception { - - Patient patient = new Patient(); - patient.setId("001"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?_id=001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was:\n{}", responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("Patient?_id=001",ourConditionalUrl); - assertEquals(null, ourId); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testUpdateMissingIdInBody() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - - OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals(Msg.code(419) + "Can not update resource, resource body must contain an ID element for update (PUT) operation", oo.getIssue().get(0).getDiagnostics()); - } - - @Test - public void testUpdateNormal() throws Exception { - - Patient patient = new Patient(); - patient.setId("001"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was:\n{}", responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertNull(ourConditionalUrl); - assertEquals("Patient/001", ourId.getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testUpdateWrongIdInBody() throws Exception { - - Patient patient = new Patient(); - patient.setId("Patient/3/_history/4"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/1/_history/2"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("Resource body ID of "3" does not match")); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Update() - public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { - ourId = theId; - ourConditionalUrl = theConditionalUrl; - IdType id = theId != null ? theId.withVersion(thePatient.getIdentifier().get(0).getValue()) : new IdType("Patient/1"); - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OODETAILS"); - if (id.getValueAsString().contains("CREATE")) { - return new MethodOutcome(id, oo, true); - } - - return new MethodOutcome(id, oo); - } - - } -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java deleted file mode 100644 index 9f928939725..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java +++ /dev/null @@ -1,327 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.ValidationModeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.CodeType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Organization; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ValidateDstu2_1Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu2_1Test.class); - public static Patient ourLastPatient; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static EncodingEnum ourLastEncoding; - private static IdType ourLastId; - private static ValidationModeEnum ourLastMode; - private static String ourLastProfile; - private static String ourLastResourceBody; - private static OperationOutcome ourOutcomeToReturn; - private static int ourPort; - - private static Server ourServer; - - @BeforeEach() - public void before() { - ourLastResourceBody = null; - ourLastEncoding = null; - ourOutcomeToReturn = null; - ourLastMode = null; - ourLastProfile = null; - } - - @Test - public void testValidate() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("001"); - patient.addIdentifier().setValue("002"); - - Parameters params = new Parameters(); - params.addParameter().setName("resource").setResource(patient); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - String resp = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertThat(resp, stringContainsInOrder(" getResourceType() { - return Organization.class; - } - - @Validate() - public MethodOutcome validate(@ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) { - ourLastResourceBody = theResourceBody; - ourLastEncoding = theEncoding; - - return new MethodOutcome(new IdType("001")); - } - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Validate() - public MethodOutcome validatePatient(@ResourceParam Patient thePatient, @IdParam(optional = true) IdType theId, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile) { - - ourLastPatient = thePatient; - ourLastId = theId; - IdType id; - if (thePatient != null) { - id = new IdType(thePatient.getIdentifier().get(0).getValue()); - if (thePatient.getIdElement().isEmpty() == false) { - id = thePatient.getIdElement(); - } - } else { - id = new IdType("1"); - } - ourLastMode = theMode; - ourLastProfile = theProfile; - - MethodOutcome outcome = new MethodOutcome(id.withVersion("002")); - outcome.setOperationOutcome(ourOutcomeToReturn); - return outcome; - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - -} diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 94b617d3ad7..be3795ef8ba 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -49,8 +49,8 @@
    - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -62,36 +62,6 @@ xmlunit-core test
    - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -112,18 +82,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - javax.activation - javax.activation-api + com.helger.schematron + ph-schematron-xslt test - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -372,7 +342,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index bedc07b6338..413c733a15d 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -45,14 +45,14 @@ import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.method.*; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java index e809ef4a6bc..aab91a5c8c4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java @@ -88,7 +88,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java index b4a6be35be7..24f1cbd4f44 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java @@ -5,21 +5,19 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -28,12 +26,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ApacheClientIntegrationTest { - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static StringParam ourLastName; - private static int ourPort; - private static Server ourServer; - private static String ourBase; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new VerboseLoggingInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -43,11 +49,8 @@ public class ApacheClientIntegrationTest { @Test - public void testSearchWithParam() throws Exception { - - IGenericClient client = ourCtx.newRestfulGenericClient(ourBase); - - Bundle response = client.search().forResource(Patient.class).where(Patient.NAME.matches().value("FOO")).returnBundle(Bundle.class).execute(); + public void testSearchWithParam() { + Bundle response = ourServer.getFhirClient().search().forResource(Patient.class).where(Patient.NAME.matches().value("FOO")).returnBundle(Bundle.class).execute(); assertEquals("search", ourLastMethod); assertEquals("FOO", ourLastName.getValue()); assertEquals(1, response.getEntry().size()); @@ -56,29 +59,9 @@ public class ApacheClientIntegrationTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.registerInterceptor(new VerboseLoggingInterceptor()); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourBase = "http://localhost:" + ourPort; - } - public static class DummyPatientResourceProvider implements IResourceProvider { //@formatter:off @@ -87,7 +70,7 @@ public class ApacheClientIntegrationTest { ourLastMethod = "search"; ourLastName = theName; - List retVal = new ArrayList(); + List retVal = new ArrayList<>(); Patient patient = new Patient(); patient.setId("123"); patient.addName().addGiven("GIVEN"); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java index 4724e1c3e55..f96792ca743 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java @@ -13,7 +13,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.config.RequestConfig; @@ -27,12 +29,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -52,12 +55,18 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class BinaryDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static Binary ourLast; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new ResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -66,7 +75,7 @@ public class BinaryDstu2Test { @Test public void testReadWithExplicitTypeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=xml"); try (CloseableHttpResponse response = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -84,7 +93,7 @@ public class BinaryDstu2Test { @Test public void testReadWithExplicitTypeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=json"); try (CloseableHttpResponse response = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -103,7 +112,7 @@ public class BinaryDstu2Test { // posts Binary directly @Test public void testPostBinary() throws Exception { - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new ByteArrayEntity(new byte[]{1, 2, 3, 4}, ContentType.create("foo/bar", "UTF-8"))); try (CloseableHttpResponse response = ourClient.execute(http)) { @@ -122,7 +131,7 @@ public class BinaryDstu2Test { res.setContentType("text/plain"); String stringContent = ourCtx.newJsonParser().encodeResourceToString(res); - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new StringEntity(stringContent, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse response = ourClient.execute(http)) { @@ -134,14 +143,14 @@ public class BinaryDstu2Test { @Test public void testBinaryReadAcceptMissing() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); binaryRead(http); } @Test public void testBinaryReadAcceptBrowser() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); http.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); @@ -161,7 +170,7 @@ public class BinaryDstu2Test { @Test public void testBinaryReadAcceptFhirJson() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); http.addHeader("Accept", Constants.CT_FHIR_JSON); @@ -177,7 +186,7 @@ public class BinaryDstu2Test { @Test public void testSearchJson() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true&_format=json"); try (CloseableHttpResponse response = ourClient.execute(http)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -196,7 +205,7 @@ public class BinaryDstu2Test { @Test public void testSearchXml() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true"); try (CloseableHttpResponse response = ourClient.execute(http)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -247,38 +256,7 @@ public class BinaryDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ResourceProvider binaryProvider = new ResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - - int timeout = 60; - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(timeout * 1000) - .setConnectionRequestTimeout(timeout * 1000) - .setSocketTimeout(timeout * 1000).build(); - - builder.setConnectionManager(connectionManager); - ourClient = builder.setDefaultRequestConfig(config).build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java index d0608fb2839..dd6bb801858 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -17,11 +19,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -34,16 +37,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class BundleTypeInResponseTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BundleTypeInResponseTest.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,35 +69,9 @@ public class BundleTypeInResponseTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Search diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java index 96d6cc85f09..1e07325b03c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,12 +21,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -39,15 +42,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class CompartmentDstu2Test { - private static CloseableHttpClient ourClient; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompartmentDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new TempPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @AfterAll public static void afterClassClearContext() { TestUtil.randomizeLocaleAndTimezone(); @@ -63,9 +72,7 @@ public class CompartmentDstu2Test { @Test public void testReadFirst() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -78,9 +85,7 @@ public class CompartmentDstu2Test { @Test public void testCompartmentSecond() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/Encounter"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/Encounter"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,9 +99,7 @@ public class CompartmentDstu2Test { @Test public void testCompartmentSecond2() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/Observation"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/Observation"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -108,33 +111,6 @@ public class CompartmentDstu2Test { assertThat(responseContent, containsString(" getResourceType() { @@ -178,4 +154,5 @@ public class CompartmentDstu2Test { } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java index 60e487dbf19..96be8d7097c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java @@ -12,8 +12,11 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -25,12 +28,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -40,21 +44,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -/** - * Created by dsotnikov on 2/25/2014. - */ public class CreateConditionalTest { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2(); + + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastConditionalUrl; private static IdDt ourLastId; private static IdDt ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class); - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -72,7 +79,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -84,8 +91,8 @@ public class CreateConditionalTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -99,7 +106,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient?_format=true&_pretty=true"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient?_format=true&_pretty=true"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -110,8 +117,8 @@ public class CreateConditionalTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals(null, ourLastId.toUnqualified().getValue()); assertEquals(null, ourLastIdParam); @@ -125,7 +132,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -144,33 +151,10 @@ public class CreateConditionalTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java index 13c9a918b6f..20104a2fa79 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java @@ -8,8 +8,11 @@ import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; @@ -17,12 +20,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,14 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class DeleteDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static boolean ourInvoked; private static String ourLastConditionalUrl; private static IdDt ourLastIdParam; - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -53,7 +64,7 @@ public class DeleteDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); HttpResponse status = ourClient.execute(httpPost); @@ -68,7 +79,7 @@ public class DeleteDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient/2"); HttpResponse status = ourClient.execute(httpPost); @@ -82,33 +93,10 @@ public class DeleteDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Delete() diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java index 73dccb78629..7e6188e0eec 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java @@ -15,7 +15,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.Header; @@ -25,35 +26,33 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Date; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class ETagServerTest { - private static CloseableHttpClient ourClient; - private static PoolingHttpClientConnectionManager ourConnectionManager; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; private static Date ourLastModifiedDate; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach @@ -65,7 +64,7 @@ public class ETagServerTest { public void testAutomaticNotModified() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_NONE_MATCH, "\"222\""); HttpResponse status = ourClient.execute(httpGet); try { @@ -82,7 +81,7 @@ public class ETagServerTest { public void testETagHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -104,7 +103,7 @@ public class ETagServerTest { public void testLastModifiedHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -132,7 +131,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"221\""); CloseableHttpResponse status = ourClient.execute(http); @@ -154,7 +153,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"222\""); CloseableHttpResponse status = ourClient.execute(http); @@ -176,7 +175,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(http); try { @@ -191,34 +190,9 @@ public class ETagServerTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(FhirContext.forDstu2()); - ourCtx = servlet.getFhirContext(); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourConnectionManager = new PoolingHttpClientConnectionManager(50000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(ourConnectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Read(version = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java index 9627fcc509f..cd3aa5a55ad 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java @@ -7,7 +7,10 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -16,12 +19,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -38,13 +42,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class IncludeAndRevincludeParameterTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static Set ourIncludes; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeAndRevincludeParameterTest.class); - private static int ourPort; private static Set ourReverseIncludes; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -55,7 +66,7 @@ public class IncludeAndRevincludeParameterTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -67,7 +78,7 @@ public class IncludeAndRevincludeParameterTest { @Test public void testWithBoth() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -82,33 +93,9 @@ public class IncludeAndRevincludeParameterTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(ourPort); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - + public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java index 03f226af1f9..cf19e290f8c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java @@ -21,7 +21,9 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.TestUtil; @@ -32,12 +34,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -54,14 +57,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyDiagnosticReportResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); HttpResponse status = ourClient.execute(httpGet); assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -69,7 +81,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -96,7 +108,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -124,7 +136,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -150,7 +162,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -177,7 +189,7 @@ public class IncludeDstu2Test { @Test @Disabled public void testMixedContainedAndNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/DiagnosticReport?_query=stitchedInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/DiagnosticReport?_query=stitchedInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,7 +203,7 @@ public class IncludeDstu2Test { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -207,7 +219,7 @@ public class IncludeDstu2Test { @Test public void testOneIncludeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -227,7 +239,7 @@ public class IncludeDstu2Test { @Test public void testOneIncludeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -247,7 +259,7 @@ public class IncludeDstu2Test { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -451,34 +463,8 @@ public class IncludeDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java index caeac0d1e9b..c8534a9bdc4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,29 +21,39 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class OperationDuplicateServerDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testOperationsAreCollapsed() throws Exception { // Metadata { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -57,7 +69,7 @@ public class OperationDuplicateServerDstu2Test { // OperationDefinition { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -78,37 +90,9 @@ public class OperationDuplicateServerDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new OrganizationProvider()); - servlet.setPlainProviders(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class BaseProvider { @Operation(name = "$myoperation", idempotent = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java index 388db6332fe..36236934379 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java @@ -21,9 +21,8 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -32,21 +31,14 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.blankOrNullString; @@ -59,8 +51,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; private static String ourLastMethod; @@ -70,9 +61,17 @@ public class OperationServerDstu2Test { private static MoneyDt ourLastParamMoney1; private static UnsignedIntDt ourLastParamUnsignedInt1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private IGenericClient myFhirClient; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -83,23 +82,17 @@ public class OperationServerDstu2Test { ourLastParamMoney1 = null; ourLastId = null; ourLastMethod = ""; - - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); } @Test public void testConformance() throws Exception { - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLogResponseBody(true); - myFhirClient.registerInterceptor(loggingInterceptor); - - Conformance p = myFhirClient.fetchConformance().ofType(Conformance.class).prettyPrint().execute(); + Conformance p = ourServer.getFhirClient().fetchConformance().ofType(Conformance.class).prettyPrint().execute(); List ops = p.getRest().get(0).getOperation(); assertThat(ops.size(), greaterThan(1)); assertNull(ops.get(0).getDefinition().getReference().getBaseUrl()); assertThat(ops.get(0).getDefinition().getReference().getValue(), startsWith("OperationDefinition/")); - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId(ops.get(0).getDefinition().getReference()).execute(); + OperationDefinition def = ourServer.getFhirClient().read().resource(OperationDefinition.class).withId(ops.get(0).getDefinition().getReference()).execute(); assertThat(def.getCode(), not(blankOrNullString())); List opNames = toOpNames(ops); @@ -113,7 +106,7 @@ public class OperationServerDstu2Test { */ @Test public void testOperationDefinition() { - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); + OperationDefinition def = ourServer.getFhirClient().read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def)); @@ -158,7 +151,7 @@ public class OperationServerDstu2Test { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -173,7 +166,7 @@ public class OperationServerDstu2Test { @Test public void testInstanceEverythingHapiClient() throws Exception { - Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdDt("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + Parameters p = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdDt("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); Bundle b = (Bundle) p.getParameterFirstRep().getResource(); assertEquals("instance $everything", ourLastMethod); @@ -186,7 +179,7 @@ public class OperationServerDstu2Test { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -202,7 +195,7 @@ public class OperationServerDstu2Test { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -219,7 +212,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(new IntegerDt(123)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -238,7 +231,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -258,7 +251,7 @@ public class OperationServerDstu2Test { * Against type should fail */ - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); status = ourClient.execute(httpPost); @@ -276,7 +269,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -301,7 +294,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -325,7 +318,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -348,7 +341,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -371,7 +364,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -389,7 +382,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -402,7 +395,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -419,7 +412,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -438,7 +431,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM3").setValue(new StringDt("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -463,7 +456,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(new IntegerDt("123")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -484,7 +477,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(money); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT2"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -499,7 +492,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithProfileDatatypeUrl() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT?PARAM1=123"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -516,7 +509,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -532,7 +525,7 @@ public class OperationServerDstu2Test { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -543,38 +536,9 @@ public class OperationServerDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java index 441964a2290..34c99f7cfec 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringOrListParam; @@ -20,7 +21,9 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -33,16 +36,18 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -55,15 +60,22 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class OperationServerWithSearchParamTypesDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static List ourLastParamValStr; private static List ourLastParamValTok; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -97,7 +109,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$andlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -123,7 +135,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); @@ -206,7 +218,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOKA|VALTOKB")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$nonrepeating"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -227,7 +239,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -247,7 +259,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -276,7 +288,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$orlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -302,7 +314,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); @@ -328,35 +340,9 @@ public class OperationServerWithSearchParamTypesDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Operation(name = "$andlist", idempotent = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java index 07079f0e3e2..e41f47492a1 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java @@ -27,7 +27,7 @@ public class PatientResourceProvider implements IResourceProvider //@formatter:off @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java index 300dacfa4d3..74fda07024c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java @@ -12,7 +12,9 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; @@ -23,12 +25,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -41,13 +44,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static boolean ourInitializeProfileList; private static IdDt ourLastId; - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -65,7 +75,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed before the later time of 2012-01-01T13:00:00Z // so we expect a 304 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T13:00:00Z").getValue())); status = ourClient.execute(httpGet); try { @@ -77,7 +87,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed at the same time of 2012-01-01T12:12:12Z // so we expect a 304 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T12:12:12Z").getValue())); status = ourClient.execute(httpGet); try { @@ -89,7 +99,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed after the earlier time of 2012-01-01T10:00:00Z // so we expect a 200 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T10:00:00Z").getValue())); status = ourClient.execute(httpGet); try { @@ -107,7 +117,7 @@ public class ReadDstu2Test { public void testAddProfile() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -130,7 +140,7 @@ public class ReadDstu2Test { ourInitializeProfileList = true; ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -150,7 +160,7 @@ public class ReadDstu2Test { public void testReadJson() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); ourLog.info(responseContent); @@ -167,7 +177,7 @@ public class ReadDstu2Test { */ @Test public void testReadXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -183,7 +193,7 @@ public class ReadDstu2Test { public void testVread() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -234,33 +244,7 @@ public class ReadDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java index 8e330e82ddc..8186316569b 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java @@ -11,8 +11,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java index e8a575a9833..f9167179b46 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -17,13 +19,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -35,14 +38,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchCountParamDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static Integer ourLastParam; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,23 +61,23 @@ public class SearchCountParamDstu2Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("search", ourLastMethod); - assertEquals(new Integer(2), ourLastParam); + assertEquals(Integer.valueOf(2), ourLastParam); assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } finally { @@ -81,7 +91,7 @@ public class SearchCountParamDstu2Test { */ @Test public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithNoCountParam&_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -94,11 +104,11 @@ public class SearchCountParamDstu2Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); //@formatter:on @@ -110,35 +120,9 @@ public class SearchCountParamDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index d28dbf5505b..1235ae59f4d 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -22,7 +22,9 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -40,13 +42,14 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import javax.annotation.Nonnull; import java.nio.charset.Charset; @@ -66,17 +69,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class SearchDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static DateAndListParam ourLastDateAndList; private static String ourLastMethod; private static QuantityParam ourLastQuantity; private static ReferenceParam ourLastRef; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2Test.class); - private static int ourPort; private static InstantDt ourReturnPublished; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyPatientResourceNoIdProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -84,13 +94,13 @@ public class SearchDstu2Test { ourLastDateAndList = null; ourLastRef = null; ourLastQuantity = null; - ourServlet.setIgnoreServerParsedRequestParameters(true); + ourServer.getRestfulServer().setIgnoreServerParsedRequestParameters(true); } @Test public void testSearchWithInvalidPostUrl() throws Exception { // should end with _search - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // add parameters to the post method List parameters = new ArrayList(); @@ -110,7 +120,7 @@ public class SearchDstu2Test { @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -127,7 +137,7 @@ public class SearchDstu2Test { @Test public void testEncodeConvertsReferencesToRelativeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -146,7 +156,7 @@ public class SearchDstu2Test { public void testResultBundleHasUpdateTime() throws Exception { ourReturnPublished = new InstantDt("2011-02-03T11:22:33Z"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithBundleProvider&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithBundleProvider&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -157,7 +167,7 @@ public class SearchDstu2Test { @Test public void testResultBundleHasUuid() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -169,7 +179,7 @@ public class SearchDstu2Test { @Test public void testSearchBlacklist01Failing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.black1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchBlacklist01&ref.black1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -179,7 +189,7 @@ public class SearchDstu2Test { @Test public void testSearchBlacklist01Passing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.white1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchBlacklist01&ref.white1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -190,7 +200,7 @@ public class SearchDstu2Test { @Test public void testSearchByPost() throws Exception { - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -209,7 +219,7 @@ public class SearchDstu2Test { @Test public void testSearchMethodReturnsNull() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchReturnNull"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchReturnNull"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -221,7 +231,7 @@ public class SearchDstu2Test { @Test public void testSearchByPostWithBodyAndUrlParams() throws Exception { - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=json"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -241,9 +251,9 @@ public class SearchDstu2Test { @Test public void testSearchByPostWithBodyAndUrlParamsNoManual() throws Exception { - ourServlet.setIgnoreServerParsedRequestParameters(false); + ourServer.getRestfulServer().setIgnoreServerParsedRequestParameters(false); - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=json"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -263,7 +273,7 @@ public class SearchDstu2Test { @Test public void testSearchByPut() throws Exception { - HttpPut httpGet = new HttpPut("http://localhost:" + ourPort + "/Patient/_search"); + HttpPut httpGet = new HttpPut(ourServer.getBaseUrl() + "/Patient/_search"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -276,7 +286,7 @@ public class SearchDstu2Test { @Test public void testSearchDateAndList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -296,7 +306,7 @@ public class SearchDstu2Test { public void testSearchPagesAllHaveCorrectBundleType() throws Exception { Bundle resp; { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchHugeResults=yes&_count=10&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?searchHugeResults=yes&_count=10&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -341,7 +351,7 @@ public class SearchDstu2Test { */ @Test public void testSearchQuantityMissingTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?quantity:missing=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?quantity:missing=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -355,7 +365,7 @@ public class SearchDstu2Test { */ @Test public void testSearchQuantityValue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?quantity=gt100"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?quantity=gt100"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -368,7 +378,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams01() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -381,7 +391,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams02() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref=Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -394,7 +404,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams03() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref:Patient=Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -407,7 +417,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams04() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref:Patient=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -423,7 +433,7 @@ public class SearchDstu2Test { */ @Test public void testSearchByIdExact() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&reference=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&reference=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -435,7 +445,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedIdQualifiedString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam:exact=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&stringParam:exact=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -447,7 +457,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam:exact=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa&stringParam:exact=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -459,7 +469,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedIdString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&stringParam=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -471,7 +481,7 @@ public class SearchDstu2Test { @Test public void testSearchByIdString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa&stringParam=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -484,7 +494,7 @@ public class SearchDstu2Test { @Test public void testSearchWhitelist01Failing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWhitelist01&ref=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -494,7 +504,7 @@ public class SearchDstu2Test { @Test public void testSearchWhitelist01Passing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref.white1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWhitelist01&ref.white1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -505,36 +515,9 @@ public class SearchDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - DummyPatientResourceNoIdProvider patientResourceNoIdProviderProvider = new DummyPatientResourceNoIdProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setResourceProviders(patientResourceNoIdProviderProvider, patientProvider); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceNoIdProvider implements IResourceProvider { @Override @@ -698,7 +681,7 @@ public class SearchDstu2Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java index c04e524443f..52f0bb07be2 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.api.AddProfileTagEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.dstu2.resource.Bundle; @@ -14,7 +15,9 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.PatientProfileDstu2; import org.apache.commons.io.IOUtils; @@ -24,12 +27,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Collections; @@ -43,16 +49,34 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchReturningProfiledResourceDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchReturningProfiledResourceDstu2Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach + public void before() { + ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); + } + + @AfterEach + public void after() { + ourCtx.setDefaultTypeForProfile("http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient", null); + } + + @Test public void testClientTypedRequest() throws Exception { - ourCtx = FhirContext.forDstu2(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(PatientProfileDstu2.class).returnBundle(Bundle.class).execute(); assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass()); @@ -60,9 +84,8 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testClientUntypedRequestWithHint() throws Exception { - ourCtx = FhirContext.forDstu2(); ourCtx.setDefaultTypeForProfile("http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient", PatientProfileDstu2.class); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute(); assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass()); @@ -70,8 +93,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testClientUntypedRequestWithoutHint() throws Exception { - ourCtx = FhirContext.forDstu2(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute(); assertEquals(Patient.class, bundle.getEntry().get(0).getResource().getClass()); @@ -79,7 +101,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testProfilesGetAdded() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -93,7 +115,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testProfilesGetAddedHtml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=html"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=html"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -107,34 +129,9 @@ public class SearchReturningProfiledResourceDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.registerInterceptor(new ResponseHighlighterInterceptor()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java index f609c3583a1..9a5b91e2228 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -14,11 +17,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -28,15 +32,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithDstu2BundleTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithDstu2BundleTest.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -52,7 +63,7 @@ public class SearchWithDstu2BundleTest { "", "" , "", - "", + "", "" , "" , "" , @@ -65,33 +76,9 @@ public class SearchWithDstu2BundleTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - /** * Created by dsotnikov on 2/25/2014. */ diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java index 1cd0e34a250..8cc362ad2bd 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java @@ -8,25 +8,19 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,12 +28,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -51,7 +52,7 @@ public class SearchWithGenericListDstu2Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,42 +60,16 @@ public class SearchWithGenericListDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java index 4e034ac93dc..2bb11ab7f97 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java @@ -60,9 +60,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -731,7 +731,7 @@ public class ServerConformanceProviderDstu2Test { public static class MultiTypeEncounterProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @OperationParam(name = "someOpParam1") DateDt theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { return null; } @@ -742,7 +742,7 @@ public class ServerConformanceProviderDstu2Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Encounter thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Encounter thePatient) { return null; } @@ -751,7 +751,7 @@ public class ServerConformanceProviderDstu2Test { public static class MultiTypePatientProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @OperationParam(name = "someOpParam1") DateDt theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { return null; } @@ -762,7 +762,7 @@ public class ServerConformanceProviderDstu2Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Patient thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Patient thePatient) { return null; } @@ -795,7 +795,7 @@ public class ServerConformanceProviderDstu2Test { public static class PlainProviderWithExtendedOperationOnNoType { @Operation(name = "plain", idempotent = true, returnParameters = {@OperationParam(min = 1, max = 2, name = "out1", type = StringDt.class)}) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { return null; } @@ -804,7 +804,7 @@ public class ServerConformanceProviderDstu2Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { return null; } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java index 03e45b6bafe..1492d1f9810 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java @@ -7,7 +7,9 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -18,11 +20,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -38,17 +41,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class ServerFeaturesDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerFeaturesDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testOptions() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + ""); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + ""); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,7 +68,7 @@ public class ServerFeaturesDstu2Test { * Now with a leading / */ - httpGet = new HttpOptions("http://localhost:" + ourPort + "/"); + httpGet = new HttpOptions(ourServer.getBaseUrl() + "/"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -76,7 +84,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath1() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/Foo"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/Foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -90,7 +98,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath2() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/Patient/1"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -104,7 +112,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath3() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/metadata"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/metadata"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -115,7 +123,7 @@ public class ServerFeaturesDstu2Test { @Test public void testOptionsJson() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "?_format=json"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -126,7 +134,7 @@ public class ServerFeaturesDstu2Test { @Test public void testHeadJson() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/123"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); @@ -138,60 +146,45 @@ public class ServerFeaturesDstu2Test { @Test public void testRegisterAndUnregisterResourceProviders() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("PRP1")); - Collection providers = new ArrayList(ourServlet.getResourceProviders()); - for (IResourceProvider provider : providers) { - ourServlet.unregisterProvider(provider); + Collection originalProviders = new ArrayList<>(ourServer.getRestfulServer().getResourceProviders()); + DummyPatientResourceProvider2 newProvider = new DummyPatientResourceProvider2(); + try { + + // Replace provider + for (IResourceProvider provider : originalProviders) { + ourServer.getRestfulServer().unregisterProvider(provider); + } + ourServer.getRestfulServer().registerProvider(newProvider); + + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + IOUtils.closeQuietly(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("PRP2")); + + } finally { + + // Restore providers + ourServer.getRestfulServer().unregisterProvider(newProvider); + originalProviders.forEach(p->ourServer.getRestfulServer().registerProvider(p)); + } - - ourServlet.registerProvider(new DummyPatientResourceProvider2()); - - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("PRP2")); - } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java index 4aa3af0638f..57f4072b97a 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java @@ -4,42 +4,44 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerSearchDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static StringParam ourLastRef; private static ReferenceParam ourLastRef2; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerSearchDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -50,7 +52,7 @@ public class ServerSearchDstu2Test { @Test public void testReferenceParamMissingFalse() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param3:missing=false"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param3:missing=false"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -61,7 +63,7 @@ public class ServerSearchDstu2Test { @Test public void testReferenceParamMissingTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param3:missing=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param3:missing=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -72,7 +74,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParam1() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=param1value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param1=param1value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -83,7 +85,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParam2() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param2value&foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param2=param2value&foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,7 +96,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParamWithSpace() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param+value&foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param2=param+value&foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -105,7 +107,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchWithEncodedValue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=" + UrlUtil.escapeUrlParam("Jernelöv")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param1=" + UrlUtil.escapeUrlParam("Jernelöv")); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -116,7 +118,7 @@ public class ServerSearchDstu2Test { @Test public void testUnknownSearchParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -126,33 +128,9 @@ public class ServerSearchDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPlainProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider { @Search(allowUnknownParams=true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java index bf6cfdb4da7..a1e4e2bac42 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java @@ -13,7 +13,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -22,13 +24,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Arrays; import java.util.List; @@ -44,13 +47,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SummaryParamDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static SummaryEnum ourLastSummary; private static List ourLastSummaryList; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyMedicationOrderProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -60,7 +70,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryData() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -79,7 +89,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -96,7 +106,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationOrder/1?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -113,7 +123,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -131,7 +141,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryCount() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -148,7 +158,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryData() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.DATA.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.DATA.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -164,7 +174,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryFalse() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=false"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=false"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -180,7 +190,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -197,7 +207,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationOrder?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -213,7 +223,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTextMulti() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -230,7 +240,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TRUE.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TRUE.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -246,7 +256,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryWithTextAndOthers() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=text&_summary=data"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=text&_summary=data"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -280,7 +290,7 @@ public class SummaryParamDstu2Test { } - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { @@ -324,31 +334,8 @@ public class SummaryParamDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationOrderProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java index f0930d2f5ef..c0ccc0f1191 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java @@ -26,7 +26,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -232,7 +232,7 @@ public class TransactionWithBundleResourceParamTest { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java index ce1263d5831..771c77140c7 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java @@ -22,7 +22,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -96,7 +96,7 @@ public class TransactionWithVersionlessBundleResourceParamTest { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java index 3db6f7cd78e..29fc3ad5ead 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java @@ -14,8 +14,11 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -27,12 +30,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -43,17 +47,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UpdateDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastConditionalUrl; private static IdDt ourLastId; private static IdDt ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static InstantDt ourSetLastUpdated; - + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -71,7 +82,7 @@ public class UpdateDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -93,7 +104,7 @@ public class UpdateDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -105,7 +116,7 @@ public class UpdateDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -121,7 +132,7 @@ public class UpdateDstu2Test { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -133,7 +144,7 @@ public class UpdateDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals("Patient/2", ourLastId.toUnqualified().getValue()); assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue()); @@ -144,32 +155,10 @@ public class UpdateDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java index d3738f289e0..ac9791d0550 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java @@ -15,7 +15,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -26,12 +28,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -40,15 +43,23 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class ValidateDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static EncodingEnum ourLastEncoding; private static ValidationModeEnum ourLastMode; private static String ourLastProfile; private static String ourLastResourceBody; private static BaseOperationOutcome ourOutcomeToReturn; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -70,7 +81,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -92,7 +103,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -115,7 +126,7 @@ public class ValidateDstu2Test { params.addParameter().setName("profile").setValue(new StringDt("http://foo")); params.addParameter().setName("mode").setValue(new StringDt(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -142,7 +153,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,32 +167,9 @@ public class ValidateDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class OrganizationProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java index 8209c5c1ee5..7aacbea52d6 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java @@ -19,12 +19,17 @@ import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.SummaryParamDstu2Test; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -33,12 +38,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -60,20 +66,27 @@ import static org.junit.jupiter.api.Assertions.assertSame; public class InterceptorUserDataMapDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InterceptorUserDataMapDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private final Object myKey = "KEY"; private final Object myValue = "VALUE"; private Map myMap; private Set myMapCheckMethods; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { - servlet.getInterceptorService().unregisterAllInterceptors(); - servlet.getInterceptorService().registerInterceptor(new MyInterceptor()); + ourServer.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().registerInterceptor(new MyInterceptor()); } @@ -87,7 +100,7 @@ public class InterceptorUserDataMapDstu2Test { @Test public void testException() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -98,7 +111,7 @@ public class InterceptorUserDataMapDstu2Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { @@ -269,31 +282,7 @@ public class InterceptorUserDataMapDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java index 41fdd43c6bd..9a64c21a654 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java @@ -19,11 +19,16 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.SummaryParamDstu2Test; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -35,13 +40,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.StringContains; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; import org.slf4j.Logger; @@ -62,17 +68,24 @@ import static org.mockito.Mockito.verify; public class LoggingInterceptorDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static int ourDelayMs; private static Exception ourThrowException; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { - servlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); ourThrowException = null; ourDelayMs=0; } @@ -86,30 +99,30 @@ public class LoggingInterceptorDstu2Test { interceptor.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}"); assertEquals("ERROR - ${requestVerb} ${requestUrl}", interceptor.getErrorMessageFormat()); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/EX"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/EX"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(logger, timeout(1000).times(1)).info(captor.capture()); - assertThat(captor.getAllValues().get(0), StringContains.containsString("ERROR - GET http://localhost:" + ourPort + "/Patient/EX")); + assertThat(captor.getAllValues().get(0), StringContains.containsString("ERROR - GET " + ourServer.getBaseUrl() + "/Patient/EX")); } @Test public void testMetadata() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -125,12 +138,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -147,12 +160,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -167,12 +180,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${requestId}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -187,12 +200,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${processingTimeMillis}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -209,12 +222,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${processingTimeMillis}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -231,12 +244,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); httpGet.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); @@ -252,7 +265,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -261,7 +274,7 @@ public class LoggingInterceptorDstu2Test { p.addIdentifier().setValue("VAL"); String input = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -280,7 +293,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); interceptor.setErrorMessageFormat("ERROR - ${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -291,7 +304,7 @@ public class LoggingInterceptorDstu2Test { ourThrowException = new NullPointerException("FOO"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -309,12 +322,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -330,12 +343,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -350,12 +363,12 @@ public class LoggingInterceptorDstu2Test { public void testRead() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -371,12 +384,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${idOrResourceName} - ${requestParameters}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -389,33 +402,9 @@ public class LoggingInterceptorDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - /** * Created by dsotnikov on 2/25/2014. */ @@ -436,7 +425,7 @@ public class LoggingInterceptorDstu2Test { } public Map getIdToPatient() { - Map idToPatient = new HashMap(); + Map idToPatient = new HashMap<>(); { Patient patient = createPatient1(); idToPatient.put("1", patient); @@ -538,4 +527,5 @@ public class LoggingInterceptorDstu2Test { } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java index 48b2924cf7f..1fe3ded49cf 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; @@ -16,61 +15,79 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class ServerActionInterceptorTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static IServerInterceptor ourInterceptor; - private static IGenericClient ourFhirClient; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + private IServerInterceptor myInterceptor; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach + public void before() { + myInterceptor = mock(InterceptorAdapter.class); + ourServer.registerInterceptor(myInterceptor); + + when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + } + + @AfterEach + public void after() { + ourServer.unregisterInterceptor(myInterceptor); + } @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123", details.getId().getValue()); @@ -78,14 +95,14 @@ public class ServerActionInterceptorTest { @Test public void testVRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/456"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123/_history/456", details.getId().getValue()); @@ -95,10 +112,10 @@ public class ServerActionInterceptorTest { public void testCreate() throws Exception { Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourFhirClient.create().resource(patient).execute(); + ourServer.getFhirClient().create().resource(patient).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient", details.getResourceName()); @@ -110,10 +127,10 @@ public class ServerActionInterceptorTest { public void testCreateWhereMethodHasNoResourceParam() throws Exception { Observation observation = new Observation(); observation.getCode().setText("OBSCODE"); - ourFhirClient.create().resource(observation).execute(); + ourServer.getFhirClient().create().resource(observation).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Observation", details.getResourceName()); @@ -126,10 +143,10 @@ public class ServerActionInterceptorTest { Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); patient.setId("Patient/123"); - ourFhirClient.update().resource(patient).execute(); + ourServer.getFhirClient().update().resource(patient).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient", details.getResourceName()); @@ -141,91 +158,48 @@ public class ServerActionInterceptorTest { @Test public void testHistorySystem() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture()); } @Test public void testHistoryType() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture()); assertEquals("Patient", detailsCapt.getValue().getResourceName()); } @Test public void testHistoryInstance() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture()); assertEquals("Patient", detailsCapt.getValue().getResourceName()); assertEquals("Patient/123", detailsCapt.getValue().getId().getValue()); } - @BeforeEach - public void before() { - reset(ourInterceptor); - - when(ourInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - } - @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.registerInterceptor(new ResponseHighlighterInterceptor()); - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyObservationResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourInterceptor = mock(InterceptorAdapter.class); - servlet.registerInterceptor(ourInterceptor); - - ourCtx.getRestfulClientFactory().setSocketTimeout(240 * 1000); - ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - - } public static class PlainProvider { diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 380194ed555..02408c1f044 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -155,9 +155,9 @@ as much as possible. See #283. --> - javax.servlet - javax.servlet-api - 3.1.0 + jakarta.servlet + jakarta.servlet-api + 6.0.0 true @@ -200,36 +200,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -241,7 +211,7 @@ thymeleaf test - + ca.uhn.hapi.fhir hapi-fhir-test-utilities ${project.version} @@ -250,18 +220,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api + jakarta.activation + jakarta.activation-api test diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java index c4ed58392ed..ca200ea7982 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -45,6 +45,8 @@ import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementKind; @@ -81,8 +83,6 @@ import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java index 5dd67c5faec..8e25e3754a8 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java @@ -2,14 +2,18 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; @@ -19,6 +23,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.TreeSet; @@ -29,10 +34,18 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class ContextScanningDstu3Test { - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ContextScanningDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new ObservationProvider()) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testContextDoesntScanUnneccesaryTypes() { @@ -44,7 +57,7 @@ public class ContextScanningDstu3Test { ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs); assertThat(resDefs, not(containsInRelativeOrder("Observation"))); - IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ctx.newRestfulGenericClient(ourServer.getBaseUrl()); client.read().resource(Patient.class).withId("1").execute(); resDefs = scannedResourceNames(ctx); @@ -101,7 +114,7 @@ public class ContextScanningDstu3Test { BaseRuntimeElementCompositeDefinition compositeDef = (BaseRuntimeElementCompositeDefinition) ctx.getElementDefinition("identifier"); assertFalse(compositeDef.isSealed()); - IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ctx.newRestfulGenericClient(ourServer.getBaseUrl()); client.read().resource(Patient.class).withId("1").execute(); resDefs = scannedResourceNames(ctx); @@ -141,24 +154,9 @@ public class ContextScanningDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new ObservationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java index 421a0ce66a0..64e51dcf21c 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java @@ -143,7 +143,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java index 2cf05c1c35e..933df506788 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java @@ -4,8 +4,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -16,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; @@ -26,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -34,13 +38,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class CreateBinaryDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static Binary ourLastBinary; private static byte[] ourLastBinaryBytes; private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new BinaryProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -51,7 +62,7 @@ public class CreateBinaryDstu3Test { @Test public void testRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); post.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(post); @@ -75,7 +86,7 @@ public class CreateBinaryDstu3Test { b.setContent(new byte[] { 0, 1, 2, 3, 4 }); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -98,7 +109,7 @@ public class CreateBinaryDstu3Test { b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -114,7 +125,7 @@ public class CreateBinaryDstu3Test { @Test public void testRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); CloseableHttpResponse status = ourClient.execute(post); try { @@ -127,31 +138,9 @@ public class CreateBinaryDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - public static class BinaryProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java index b1691ef0ad2..bffb7aa199b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java @@ -7,9 +7,12 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -21,8 +24,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.DateType; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -34,6 +37,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -47,14 +51,21 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class CreateDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class); - private static int ourPort; - private static Server ourServer; public static IBaseOperationOutcome ourReturnOo; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOo = null; @@ -66,7 +77,7 @@ public class CreateDstu3Test { @Test public void testCreateReturnsLocationHeader() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -79,7 +90,7 @@ public class CreateDstu3Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @@ -88,7 +99,7 @@ public class CreateDstu3Test { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"gender\":\"male\"}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -108,7 +119,7 @@ public class CreateDstu3Test { @Test public void testCreateWithInvalidContent() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -127,7 +138,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent1() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -146,7 +157,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent2() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -165,7 +176,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent3() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -183,7 +194,7 @@ public class CreateDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -211,34 +222,10 @@ public class CreateDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { + private static class PatientProvider implements IResourceProvider { @Create() public MethodOutcome create(@ResourceParam Patient theIdParam) { @@ -263,7 +250,7 @@ public class CreateDstu3Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java index ab4e3d9c0bb..9bae98c5f28 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java @@ -10,8 +10,11 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -22,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; @@ -31,6 +34,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -40,16 +44,23 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class CustomTypeServerDstu3 { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastConditionalUrl; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu3.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -67,7 +78,7 @@ public class CustomTypeServerDstu3 { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -87,7 +98,7 @@ public class CustomTypeServerDstu3 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -109,7 +120,7 @@ public class CustomTypeServerDstu3 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -128,32 +139,9 @@ public class CustomTypeServerDstu3 { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java index 26fc80d2b57..12b738269b5 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java @@ -4,17 +4,20 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -22,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -29,16 +33,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class DeleteConditionalDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static IGenericClient ourHapiClient; private static String ourLastConditionalUrl; private static IdType ourLastIdParam; private static boolean ourLastRequestWasDelete; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu3Test.class); - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -55,23 +66,13 @@ public class DeleteConditionalDstu3Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); -// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); -// -// HttpResponse status = ourClient.execute(httpGet); -// -// String responseContent = IOUtils.toString(status.getEntity().getContent()); -// IOUtils.closeQuietly(status.getEntity().getContent()); -// -// ourLog.info("Response was:\n{}", responseContent); - - //@formatter:off - ourHapiClient + ourServer + .getFhirClient() .delete() .resourceConditionalByType(Patient.class) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS","SOMEID")) .execute(); - //@formatter:on - + assertTrue(ourLastRequestWasDelete); assertEquals(null, ourLastIdParam); assertEquals("Patient?identifier=SOMESYS%7CSOMEID", ourLastConditionalUrl); @@ -82,36 +83,10 @@ public class DeleteConditionalDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - public static class PatientProvider implements IResourceProvider { @Delete() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java index f9fb9b0cfb1..78ada9acf20 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java @@ -5,24 +5,17 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,21 +23,26 @@ public class FormatParameterDstu3Test { private static final String VALUE_XML = ""; private static final String VALUE_JSON = "{\"resourceType\":\"Patient\",\"id\":\"p1ReadId\",\"meta\":{\"profile\":[\"http://foo_profile\"]},\"identifier\":[{\"value\":\"p1ReadValue\"}]}"; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu3Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); /** * See #346 */ @Test public void testFormatXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=xml"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -62,9 +60,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -82,9 +80,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXmlFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml%2Bfhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml%2Bfhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -102,10 +100,10 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXmlFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml+fhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml+fhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -123,9 +121,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=json"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -143,9 +141,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -163,9 +161,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJsonFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json%2Bfhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json%2Bfhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -183,10 +181,10 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJsonFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json+fhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json+fhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -201,34 +199,10 @@ public class FormatParameterDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java index 1f636645ada..f720e5851b7 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java @@ -21,7 +21,9 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -33,8 +35,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IntegerType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -46,11 +48,12 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; @@ -73,18 +76,23 @@ import static org.mockito.Mockito.when; public class InterceptorDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static Patient ourLastPatient; private IServerInterceptor myInterceptor1; private IServerInterceptor myInterceptor2; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @AfterEach public void after() { - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } @BeforeEach @@ -115,20 +123,20 @@ public class InterceptorDstu3Test { resource.set(requestDetails.getResource()); }; - ourServlet.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, interceptor); + ourServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, interceptor); try { Parameters p = new Parameters(); p.addParameter().setName("limit").setValue(new IntegerType(123)); String input = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Patient/$postOperation"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Patient/$postOperation"); post.setEntity(new StringEntity(input, ContentType.create("application/fhir+json", Constants.CHARSET_UTF8))); try (CloseableHttpResponse status = ourClient.execute(post)) { assertEquals(200, status.getStatusLine().getStatusCode()); IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); } } finally { - ourServlet.unregisterInterceptor(interceptor); + ourServer.unregisterInterceptor(interceptor); } assertNotNull(resource.get()); @@ -148,10 +156,10 @@ public class InterceptorDstu3Test { return true; } }; - ourServlet.registerInterceptor(interceptor); + ourServer.registerInterceptor(interceptor); try { - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); try (CloseableHttpResponse status = ourClient.execute(get)) { String response = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); assertThat(response, containsString("NAME1")); @@ -160,13 +168,13 @@ public class InterceptorDstu3Test { } } finally { - ourServlet.unregisterInterceptor(interceptor); + ourServer.unregisterInterceptor(interceptor); } } @Test public void testResourceResponseIncluded() throws Exception { - ourServlet.setInterceptors(myInterceptor1, myInterceptor2); + ourServer.getRestfulServer().setInterceptors(myInterceptor1, myInterceptor2); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -192,7 +200,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -214,7 +222,7 @@ public class InterceptorDstu3Test { @Test public void testExceptionInProcessingCompletedNormally() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -226,7 +234,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -236,7 +244,7 @@ public class InterceptorDstu3Test { @Test public void testResponseWithNothing() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -248,7 +256,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -272,7 +280,7 @@ public class InterceptorDstu3Test { @Test public void testResponseWithOperationOutcome() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -284,7 +292,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -315,33 +323,9 @@ public class InterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java index 62f7c77ac8a..df9e31cd42c 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.VersionUtil; import org.apache.commons.io.IOUtils; @@ -22,8 +24,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.Patient; @@ -31,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; @@ -45,11 +48,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MetadataCapabilityStatementDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataCapabilityStatementDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false) + .withServer(s->s.setServerConformanceProvider(new ServerCapabilityStatementProvider(s).setCache(false))); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); static { HapiSystemProperties.enableTestMode(); @@ -57,14 +67,14 @@ public class MetadataCapabilityStatementDstu3Test { @AfterEach public void after() { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); } @Test public void testElements() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_elements=fhirVersion&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -81,7 +91,7 @@ public class MetadataCapabilityStatementDstu3Test { public void testHttpMethods() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -94,7 +104,7 @@ public class MetadataCapabilityStatementDstu3Test { } try { - httpPost = new HttpPost("http://localhost:" + ourPort + "/metadata"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/metadata"); status = ourClient.execute(httpPost); output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -110,7 +120,7 @@ public class MetadataCapabilityStatementDstu3Test { * would be interpreted as a read on ID "metadata" */ try { - httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/metadata"); status = ourClient.execute(httpPost); output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(400, status.getStatusLine().getStatusCode()); @@ -123,7 +133,7 @@ public class MetadataCapabilityStatementDstu3Test { public void testResponseContainsBaseUrl() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_format=json"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_format=json"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -131,7 +141,7 @@ public class MetadataCapabilityStatementDstu3Test { ourLog.info(output); CapabilityStatement cs = ourCtx.newJsonParser().parseResource(CapabilityStatement.class, output); - assertEquals("http://localhost:" + ourPort + "/", cs.getImplementation().getUrl()); + assertEquals(ourServer.getBaseUrl() + "/", cs.getImplementation().getUrl()); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } @@ -139,11 +149,11 @@ public class MetadataCapabilityStatementDstu3Test { @Test public void testResponseContainsBaseUrlFixed() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://foo/bar")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://foo/bar")); String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_format=json"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_format=json"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -162,7 +172,7 @@ public class MetadataCapabilityStatementDstu3Test { String output; // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_summary=true&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -176,7 +186,7 @@ public class MetadataCapabilityStatementDstu3Test { } // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -192,36 +202,9 @@ public class MetadataCapabilityStatementDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ourServlet.setServerConformanceProvider(new ServerCapabilityStatementProvider(ourServlet).setCache(false)); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - @SuppressWarnings("unused") public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java index 3a3203833da..60bfcf009e9 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java @@ -9,7 +9,8 @@ import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.VersionUtil; import org.apache.commons.io.IOUtils; @@ -18,21 +19,13 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -43,18 +36,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MetadataConformanceDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSummary() throws Exception { String output; // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_summary=true&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -68,7 +67,7 @@ public class MetadataConformanceDstu3Test { } // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -86,7 +85,7 @@ public class MetadataConformanceDstu3Test { public void testElements() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_elements=fhirVersion&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -103,7 +102,7 @@ public class MetadataConformanceDstu3Test { public void testHttpMethods() throws Exception { String output; - HttpRequestBase httpOperation = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpRequestBase httpOperation = new HttpGet(ourServer.getBaseUrl() + "/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -112,7 +111,7 @@ public class MetadataConformanceDstu3Test { assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("REST Server (FHIR Server; FHIR " + ourCtx.getVersion().getVersion().getFhirVersionString() + "/" + ourCtx.getVersion().getVersion().name() + ")")); } - httpOperation = new HttpOptions("http://localhost:" + ourPort); + httpOperation = new HttpOptions(ourServer.getBaseUrl()); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -121,7 +120,7 @@ public class MetadataConformanceDstu3Test { assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("REST Server (FHIR Server; FHIR " + ourCtx.getVersion().getVersion().getFhirVersionString() + "/" + ourCtx.getVersion().getVersion().name() + ")")); } - httpOperation = new HttpPost("http://localhost:" + ourPort + "/metadata"); + httpOperation = new HttpPost(ourServer.getBaseUrl() + "/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -132,7 +131,7 @@ public class MetadataConformanceDstu3Test { * There is no @read on the RP below, so this should fail. Otherwise it * would be interpreted as a read on ID "metadata" */ - httpOperation = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata"); + httpOperation = new HttpGet(ourServer.getBaseUrl() + "/Patient/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -159,32 +158,7 @@ public class MetadataConformanceDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setServerConformanceProvider(new ServerCapabilityStatementProvider(ourServlet)); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java index a4f1f28041c..f8acf9eaa86 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -23,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestOperationComponent; @@ -42,6 +44,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -57,8 +60,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static IdType ourLastId; private static String ourLastMethod; @@ -68,10 +70,19 @@ public class OperationServerDstu3Test { private static Money ourLastParamMoney1; private static UnsignedIntType ourLastParamUnsignedInt1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); private IGenericClient myFhirClient; + @BeforeEach public void before() { ourLastParam1 = null; @@ -82,7 +93,7 @@ public class OperationServerDstu3Test { ourLastId = null; ourLastMethod = ""; - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + myFhirClient = ourServer.getFhirClient(); } @@ -155,7 +166,7 @@ public class OperationServerDstu3Test { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -170,7 +181,7 @@ public class OperationServerDstu3Test { @Test public void testInstanceEverythingHapiClient() throws Exception { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); assertEquals("instance $everything", ourLastMethod); assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue()); @@ -183,7 +194,7 @@ public class OperationServerDstu3Test { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -199,7 +210,7 @@ public class OperationServerDstu3Test { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -216,7 +227,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(new IntegerType(123)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -235,7 +246,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -255,7 +266,7 @@ public class OperationServerDstu3Test { * Against type should fail */ - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); status = ourClient.execute(httpPost); @@ -273,7 +284,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -298,7 +309,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -322,7 +333,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -345,7 +356,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -368,7 +379,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -386,7 +397,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -399,7 +410,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -416,7 +427,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -436,7 +447,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -461,7 +472,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(new IntegerType("123")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -482,7 +493,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(money); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT2"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -497,7 +508,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithProfileDatatypeUrl() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT?PARAM1=123"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -514,7 +525,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -530,7 +541,7 @@ public class OperationServerDstu3Test { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -542,38 +553,9 @@ public class OperationServerDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.registerProvider(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java index 76335fec772..3036e082ac3 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringOrListParam; @@ -13,7 +14,9 @@ import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -26,8 +29,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.IdType; @@ -41,8 +44,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -55,15 +60,24 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class OperationServerWithSearchParamTypesDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static List ourLastParamValStr; private static List ourLastParamValTok; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = ""; @@ -95,7 +109,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$andlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -121,7 +135,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -134,7 +148,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -266,7 +280,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOKA|VALTOKB")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$nonrepeating"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -286,7 +300,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { } @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -306,7 +320,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -335,7 +349,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$orlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -361,7 +375,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -386,33 +400,9 @@ public class OperationServerWithSearchParamTypesDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - ourServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java index e82099319a4..d4b9b59e3de 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java @@ -9,46 +9,47 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.PatchTypeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPatch; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class PatchServerDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchServerDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static PatchTypeEnum ourLastPatchType; private static String ourLastBody; private static IdType ourLastId; private static String ourLastConditional; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -60,7 +61,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidJson() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -83,7 +84,7 @@ public class PatchServerDstu3Test { @Test public void testPatchUsingConditional() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient?_id=123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient?_id=123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -107,7 +108,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidXml() throws Exception { String requestContents = ""; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_XML_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -130,7 +131,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidJsonWithCharset() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -150,7 +151,7 @@ public class PatchServerDstu3Test { @Test public void testPatchInvalidMimeType() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse("text/plain; charset=UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -193,33 +194,7 @@ public class PatchServerDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java index 3675cb90c9b..9817849504b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java @@ -10,8 +10,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java index b3edc466493..5f8bfa1a944 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java @@ -6,7 +6,9 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -15,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -24,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -38,13 +41,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchCountParamDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static Integer ourLastParam; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -53,7 +63,7 @@ public class SearchCountParamDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=2"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -65,11 +75,11 @@ public class SearchCountParamDstu3Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } @@ -79,7 +89,7 @@ public class SearchCountParamDstu3Test { @Test public void testSearchCount0() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=0&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=0&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -103,7 +113,7 @@ public class SearchCountParamDstu3Test { */ @Test public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithNoCountParam&_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -115,11 +125,11 @@ public class SearchCountParamDstu3Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } finally { @@ -170,33 +180,7 @@ public class SearchCountParamDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java index ceb424dfcb4..cf4d62d6701 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java @@ -4,8 +4,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.RawParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -23,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -37,15 +41,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchDefaultMethodDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDefaultMethodDstu3Test.class); private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static StringAndListParam ourLastParam1; private static StringAndListParam ourLastParam2; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -56,7 +69,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchNoParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -76,7 +89,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchOneOptionalParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -97,7 +110,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -122,7 +135,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParamsAndExtraParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m3=val3&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2¶m3=val3&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -151,7 +164,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParamsWithQualifierAndExtraParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m2:exact=val2e¶m3=val3&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2¶m2:exact=val2e¶m3=val3&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -181,34 +194,9 @@ public class SearchDefaultMethodDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - private static Map> ourLastAdditionalParams; public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java index 9ec3e18c12f..2a5ba4c17b7 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java @@ -12,34 +12,28 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -50,14 +44,19 @@ import static org.junit.jupiter.api.Assertions.fail; public class SearchDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static TokenAndListParam ourIdentifiers; private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -67,7 +66,7 @@ public class SearchDstu3Test { @Test public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -86,7 +85,7 @@ public class SearchDstu3Test { @Test public void testSearchWithInvalidChain() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier.chain=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -111,7 +110,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=json"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=json")); @@ -143,7 +142,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); @@ -175,7 +174,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=xml"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=xml")); @@ -207,7 +206,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, not(containsString("_format"))); @@ -239,7 +238,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); @@ -290,7 +289,7 @@ public class SearchDstu3Test { @Test public void testSearchWithPostAndInvalidParameters() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setLogRequestSummary(true); interceptor.setLogRequestBody(true); @@ -320,35 +319,9 @@ public class SearchDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -362,13 +335,13 @@ public class SearchDstu3Test { @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { ourLastMethod = "search"; ourIdentifiers = theIdentifiers; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); for (int i = 0; i < 200; i++) { Patient patient = new Patient(); patient.addName(new HumanName().setFamily("FAMILY")); patient.getIdElement().setValue("Patient/" + i); - retVal.add((Patient) patient); + retVal.add(patient); } return retVal; } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java index 17af852a1cf..7346bc7a7f5 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java @@ -6,7 +6,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.param.HasAndListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -15,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -24,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -33,14 +36,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchHasParamDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static HasAndListParam ourLastParam; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -49,7 +58,7 @@ public class SearchHasParamDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_has:Encounter:patient:type=SURG"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -66,34 +75,9 @@ public class SearchHasParamDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java index 323e3f2276b..154910aec81 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java @@ -9,7 +9,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -22,8 +24,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -32,8 +34,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -54,22 +58,28 @@ public class SearchPostDstu3Test { } - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; private static StringAndListParam ourLastName; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { ourLastMethod = null; ourLastSortSpec = null; ourLastName = null; - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } /** @@ -77,7 +87,7 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsNoInterceptorsYesParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=application/fhir+json"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=application/fhir+json"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -108,7 +118,7 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsNoInterceptorsNoParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -139,9 +149,9 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsYesInterceptorsYesParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); + ourServer.registerInterceptor(new ParamLoggingInterceptor()); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=application/fhir+json"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=application/fhir+json"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -172,9 +182,9 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsYesInterceptorsNoParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); + ourServer.registerInterceptor(new ParamLoggingInterceptor()); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -202,35 +212,9 @@ public class SearchPostDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java index e225ad2feb0..ec1d974ec0f 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Sort; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -23,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -32,14 +35,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchSortDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -48,7 +57,7 @@ public class SearchSortDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_sort=param1,-param2,param3,-param4"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,34 +85,9 @@ public class SearchSortDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java index 96875620297..ed73903c2be 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,13 +28,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +52,7 @@ public class SearchWithGenericListDstu3Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,42 +60,17 @@ public class SearchWithGenericListDstu3Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { @@ -109,7 +85,7 @@ public class SearchWithGenericListDstu3Test { public List searchByIdentifier( @RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) { ourLastMethod = "searchByIdentifier"; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1")); return retVal; } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java index 9a7d1db840b..555df4c1d6e 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -14,14 +16,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Organization; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -35,15 +38,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithIncludesDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearchIncludesReferences() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -63,37 +73,11 @@ public class SearchWithIncludesDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java index 10ae6e87550..86b175194a2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java @@ -3,27 +3,21 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -31,42 +25,48 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithServerAddressStrategyDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu3Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testIncomingRequestAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @Test public void testApacheProxyAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,8 +76,8 @@ public class SearchWithServerAddressStrategyDstu3Test { assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -87,8 +87,8 @@ public class SearchWithServerAddressStrategyDstu3Test { assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); httpGet.addHeader("x-forwarded-proto", "https"); status = ourClient.execute(httpGet); @@ -103,9 +103,9 @@ public class SearchWithServerAddressStrategyDstu3Test { @Test public void testHardcodedAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -118,37 +118,10 @@ public class SearchWithServerAddressStrategyDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -159,7 +132,7 @@ public class SearchWithServerAddressStrategyDstu3Test { //@formatter:off @Search() public List searchByIdentifier() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("1")); retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("2")); return retVal; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java index 2b329c2b91d..0bf146ae48a 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java @@ -9,11 +9,14 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -25,8 +28,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; @@ -36,6 +39,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -51,11 +55,17 @@ public class ServerExceptionDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu3Test.class); public static Exception ourException; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @AfterEach public void after() { @@ -72,7 +82,7 @@ public class ServerExceptionDstu3Test { .addResponseHeader("X-Foo", "BAR BAR"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -100,7 +110,7 @@ public class ServerExceptionDstu3Test { ourException = new InternalErrorException("Error", operationOutcome); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); String responseContent = new String(responseContentBytes, Charsets.UTF_8); @@ -116,7 +126,7 @@ public class ServerExceptionDstu3Test { ourException = new NullPointerException("Hello"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -133,7 +143,7 @@ public class ServerExceptionDstu3Test { ourException = new IOException("Hello"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -148,14 +158,14 @@ public class ServerExceptionDstu3Test { @Test public void testInterceptorThrowsNonHapiUncheckedExceptionHandledCleanly() throws Exception { - ourServlet.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, new IAnonymousInterceptor() { + ourServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, new IAnonymousInterceptor() { @Override public void invoke(IPointcut thePointcut, HookParams theArgs) { throw new NullPointerException("Hello"); } }); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -165,7 +175,7 @@ public class ServerExceptionDstu3Test { assertThat(responseContent, containsString("\"diagnostics\":\"Hello\"")); } - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } @@ -173,7 +183,7 @@ public class ServerExceptionDstu3Test { @Test public void testPostWithNoBody() throws IOException { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(status.getStatusLine().toString()); @@ -194,7 +204,7 @@ public class ServerExceptionDstu3Test { ourException = new AuthenticationException().addAuthenticateHeaderForRealm("REALM"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(status.getStatusLine().toString()); @@ -231,32 +241,8 @@ public class ServerExceptionDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java index 258ecf21f3e..cdef78525ce 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -25,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.DateType; @@ -37,6 +39,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -51,16 +54,22 @@ import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerMimetypeDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -91,7 +100,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -107,7 +116,7 @@ public class ServerMimetypeDstu3Test { @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -124,7 +133,7 @@ public class ServerMimetypeDstu3Test { public String getMethod() { return "TRACK"; }}; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -142,7 +151,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML_NEW + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -164,7 +173,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -186,7 +195,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -207,7 +216,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON_NEW + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -228,7 +237,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); @@ -247,7 +256,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -264,7 +273,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -281,7 +290,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -300,7 +309,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -316,7 +325,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -332,7 +341,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -347,33 +356,9 @@ public class ServerMimetypeDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } + public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java index 45e3a4ecb9b..6cd10c180cc 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java @@ -14,7 +14,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java index 4e03d07bf6e..a2ca5a8680d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java @@ -2,8 +2,11 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -12,8 +15,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; import org.hl7.fhir.dstu3.model.Patient; @@ -21,6 +24,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; @@ -32,13 +36,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class UnclassifiedServerExceptionDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu3Test.class); - private static int ourPort; - private static Server ourServer; public static UnclassifiedServerFailureException ourException; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @Test public void testSearch() throws Exception { @@ -46,7 +57,7 @@ public class UnclassifiedServerExceptionDstu3Test { operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); ourException = new UnclassifiedServerFailureException(477, "SOME MESSAGE", operationOutcome); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -63,33 +74,9 @@ public class UnclassifiedServerExceptionDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java index c18f4175cee..844069431a2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -20,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -33,6 +35,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,8 +44,7 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class ValidateDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static EncodingEnum ourLastEncoding; private static IdType ourLastId; private static ValidationModeEnum ourLastMode; @@ -51,9 +53,17 @@ public class ValidateDstu3Test { private static String ourLastResourceBody; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu3Test.class); private static OperationOutcome ourOutcomeToReturn; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach() public void before() { @@ -74,7 +84,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -97,7 +107,7 @@ public class ValidateDstu3Test { params.addParameter().setName("resource").setResource(patient); params.addParameter().setName("mode").setValue(new CodeType(" ")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -118,7 +128,7 @@ public class ValidateDstu3Test { params.addParameter().setName("resource").setResource(patient); params.addParameter().setName("mode").setValue(new CodeType("AAA")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -140,7 +150,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("mode").setValue(new CodeType("create")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,7 +166,7 @@ public class ValidateDstu3Test { ourOutcomeToReturn = new OperationOutcome(); ourOutcomeToReturn.addIssue().setDiagnostics("FOOBAR"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$validate"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$validate"); HttpResponse status = ourClient.execute(httpGet); String resp = IOUtils.toString(status.getEntity().getContent()); @@ -180,7 +190,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -203,7 +213,7 @@ public class ValidateDstu3Test { params.addParameter().setName("profile").setValue(new StringType("http://foo")); params.addParameter().setName("mode").setValue(new StringType(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -231,7 +241,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -245,32 +255,9 @@ public class ValidateDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class OrganizationProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java index a4296c69645..d5dcec14b98 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java @@ -3,10 +3,15 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.CreateDstu3Test; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,13 +24,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; import java.util.HashMap; @@ -38,17 +44,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BanUnsupprtedHttpMethodsInterceptorDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); - private static RestfulServer servlet; + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -60,7 +71,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testHeadJsonWithInvalidPatient() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/123"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); ourLog.info(status.toString()); @@ -71,7 +82,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testHeadJsonWithValidPatient() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/1"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); ourLog.info(status.toString()); @@ -88,7 +99,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { return "TRACK"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -107,7 +118,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { return "FOO"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -121,7 +132,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -130,33 +141,9 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { private Patient createPatient1() { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java index 5098740f1b5..6255287bcfc 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java @@ -28,8 +28,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.HumanName; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java index 6d3a2777a34..25fa2bb8d1d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java @@ -19,7 +19,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; @@ -31,9 +33,10 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CarePlan; @@ -52,6 +55,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,27 +70,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class AuthorizationInterceptorDstu3Test { - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"Access denied by default policy (no applicable rules)\"}]}"; - private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptorDstu3Test.class); - private static CloseableHttpClient ourClient; - private static String ourConditionalCreateId; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static boolean ourHitMethod; - private static int ourPort; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static List ourReturn; private static List ourDeleted; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - ourServlet.getInterceptorService().unregisterAllInterceptors(); - ourServlet.setTenantIdentificationStrategy(null); + ourServer.getInterceptorService().unregisterAllInterceptors(); + ourServer.getRestfulServer().setTenantIdentificationStrategy(null); ourReturn = null; ourDeleted = null; - ourHitMethod = false; - ourConditionalCreateId = "1123"; } private Resource createCarePlan(Integer theId, String theSubjectId) { @@ -176,7 +178,7 @@ public class AuthorizationInterceptorDstu3Test { @Test public void testTransactionWithPatch() throws IOException { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + ourServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() @@ -207,7 +209,7 @@ public class AuthorizationInterceptorDstu3Test { responseBundle.setType(Bundle.BundleType.TRANSACTION); ourReturn = Collections.singletonList(responseBundle); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl()); httpPost.setEntity(createFhirResourceEntity(requestBundle)); CloseableHttpResponse status = ourClient.execute(httpPost); String resp = extractResponseAndClose(status); @@ -220,31 +222,26 @@ public class AuthorizationInterceptorDstu3Test { @History() public List history() { - ourHitMethod = true; return (ourReturn); } @Operation(name = "opName", idempotent = true) public Parameters operation() { - ourHitMethod = true; return (Parameters) new Parameters().setId("1"); } @Operation(name = "process-message", idempotent = true) public Parameters processMessage(@OperationParam(name = "content") Bundle theInput) { - ourHitMethod = true; return (Parameters) new Parameters().setId("1"); } @GraphQL public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String theQuery) { - ourHitMethod = true; return "{'foo':'bar'}"; } @Transaction() public Bundle search(ServletRequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, @TransactionParam Bundle theInput) { - ourHitMethod = true; if (ourDeleted != null) { for (IBaseResource next : ourDeleted) { HookParams params = new HookParams() @@ -261,34 +258,8 @@ public class AuthorizationInterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PlainProvider plainProvider = new PlainProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.registerProvider(plainProvider); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java index 08c67a17007..7cefdb1a833 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java @@ -27,7 +27,7 @@ public class PatientResourceProvider implements IResourceProvider @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java index 916c47c286b..a88043940cf 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java @@ -74,9 +74,9 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -970,7 +970,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class MultiTypeEncounterProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { return null; } @@ -981,7 +981,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) { return null; } @@ -991,7 +991,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class MultiTypePatientProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { return null; } @@ -1002,7 +1002,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) { return null; } @@ -1037,7 +1037,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class PlainProviderWithExtendedOperationOnNoType { @Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) }) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -1048,7 +1048,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 4f4e5b841be..1b297a905e9 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -32,6 +32,13 @@ ${fhir_core_version} + + jakarta.servlet + jakarta.servlet-api + provided + true + + @@ -59,17 +66,6 @@ true - - - javax.servlet - servlet-api - 2.5 - true - - org.ogce xpp3 @@ -103,36 +99,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -153,23 +119,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -295,7 +256,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java index 4a6aaae718a..49315d13b8d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java @@ -43,6 +43,8 @@ import ca.uhn.fhir.rest.server.method.OperationParameter; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.model.Conformance; import org.hl7.fhir.dstu2.model.Conformance.*; @@ -58,8 +60,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java index b2bf88b0eb1..fe89591728f 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java @@ -9,7 +9,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Binary; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -29,6 +32,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Collections; import java.util.List; @@ -45,12 +49,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BinaryHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static Binary ourLast; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new ResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -59,7 +69,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testReadWithExplicitTypeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -76,7 +86,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testReadWithExplicitTypeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,7 +104,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testCreate() throws Exception { - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new ByteArrayEntity(new byte[]{1, 2, 3, 4}, ContentType.create("foo/bar", "UTF-8"))); HttpResponse status = ourClient.execute(http); @@ -107,7 +117,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); HttpResponse status = ourClient.execute(httpGet); byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -119,7 +129,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testSearchJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -137,7 +147,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testSearchXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,30 +201,7 @@ public class BinaryHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ResourceProvider patientProvider = new ResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java index 734e011c748..12341610839 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java @@ -4,7 +4,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -12,13 +15,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle.BundleType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -29,14 +33,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BundleTypeInResponseHl7OrgTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BundleTypeInResponseHl7OrgTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -71,32 +82,7 @@ public class BundleTypeInResponseHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java index d14a0e629c9..38103382829 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java @@ -10,8 +10,12 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -22,14 +26,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -43,18 +48,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * Created by dsotnikov on 2/25/2014. */ public class CreateConditionalHl7OrgTest { - private static CloseableHttpClient ourClient; - private static String ourLastConditionalUrl; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalHl7OrgTest.class); - private static int ourPort; - private static Server ourServer; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -70,7 +80,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -82,8 +92,8 @@ public class CreateConditionalHl7OrgTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -97,7 +107,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient?_format=true&_pretty=true"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient?_format=true&_pretty=true"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -108,8 +118,8 @@ public class CreateConditionalHl7OrgTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -123,7 +133,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -142,32 +152,10 @@ public class CreateConditionalHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java index 1722aa1034b..6ca5d14e60a 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java @@ -4,22 +4,27 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,16 +35,23 @@ import static org.junit.jupiter.api.Assertions.assertNull; * Created by dsotnikov on 2/25/2014. */ public class DeleteConditionalHl7OrgTest { - private static CloseableHttpClient ourClient; private static String ourLastConditionalUrl; - private static int ourPort; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static IdType ourLastIdParam; - - - - @BeforeEach + + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @BeforeEach public void before() { ourLastConditionalUrl = null; ourLastIdParam = null; @@ -50,7 +62,7 @@ public class DeleteConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); HttpResponse status = ourClient.execute(httpPost); @@ -66,7 +78,7 @@ public class DeleteConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient/2"); HttpResponse status = ourClient.execute(httpPost); @@ -78,32 +90,10 @@ public class DeleteConditionalHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java index 6528c42fb52..2cf3d0a2737 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java @@ -5,7 +5,11 @@ import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -13,13 +17,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -33,14 +38,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeAndRevincludeParameterHl7OrgTest { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static Set ourIncludes; private static Set ourReverseIncludes; - @BeforeEach + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourIncludes = null; ourReverseIncludes = null; @@ -48,7 +61,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,7 +73,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @Test public void testWithBoth() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -74,30 +87,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java index 39899c6b19c..362cf19d653 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java @@ -11,17 +11,13 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.ElementUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode; import org.hl7.fhir.dstu2.model.DiagnosticReport; @@ -31,9 +27,9 @@ import org.hl7.fhir.dstu2.model.Organization; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.dstu2.model.Practitioner; import org.hl7.fhir.dstu2.model.Reference; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -41,7 +37,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -50,14 +45,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -73,7 +76,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testOneIncludeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -93,7 +96,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testOneIncludeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -113,7 +116,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -139,7 +142,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -165,7 +168,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,7 +194,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -219,7 +222,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -243,7 +246,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); HttpResponse status = ourClient.execute(httpGet); assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -435,33 +438,7 @@ public class IncludeHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java index 885e0427105..4a7f6762ef5 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -13,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Conformance; import org.hl7.fhir.dstu2.model.OperationDefinition; import org.hl7.fhir.dstu2.model.Organization; @@ -25,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -32,20 +36,29 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class OperationDuplicateServerHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); static { HapiSystemProperties.enableTestMode(); } + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @Test public void testOperationsAreCollapsed() throws Exception { // Metadata { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -64,7 +77,7 @@ public class OperationDuplicateServerHl7OrgDstu2Test { // OperationDefinition { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,34 +131,7 @@ public class OperationDuplicateServerHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new OrganizationProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java index 43577d3d6b3..adc247aa472 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.IntegerType; @@ -33,6 +36,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -47,16 +51,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerHl7OrgTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerHl7OrgTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static StringType ourLastParam1; private static Patient ourLastParam2; - private static int ourPort; private static IdType ourLastId; - private static Server ourServer; private static String ourLastMethod; private static List ourLastParam3; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastParam1 = null; @@ -73,7 +85,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -91,7 +103,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -108,7 +120,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -126,7 +138,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -149,7 +161,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -167,7 +179,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -187,7 +199,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -213,7 +225,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -232,7 +244,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -250,7 +262,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -266,7 +278,7 @@ public class OperationServerHl7OrgTest { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -280,7 +292,7 @@ public class OperationServerHl7OrgTest { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -296,7 +308,7 @@ public class OperationServerHl7OrgTest { @Test public void testInstanceEverythingHapiClient() throws Exception { - Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + Parameters p = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); Bundle b = (Bundle) p.getParameter().get(0).getResource(); assertNotNull(b); @@ -309,7 +321,7 @@ public class OperationServerHl7OrgTest { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -473,34 +485,8 @@ public class OperationServerHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java index 57c3ad93858..92be0bbb272 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java @@ -6,8 +6,12 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; @@ -17,13 +21,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -33,21 +38,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class PreferHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferHl7OrgDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - - @Test + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @Test public void testCreateWithNoPrefer() throws Exception { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -58,8 +70,8 @@ public class PreferHl7OrgDstu2Test { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -68,32 +80,10 @@ public class PreferHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java index 1a13e271050..890df560551 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java @@ -3,22 +3,17 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -26,17 +21,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - /** + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + /** * In DSTU2+ the resource ID appears in the resource body */ @Test public void testReadXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -51,7 +53,7 @@ public class ReadHl7OrgDstu2Test { */ @Test public void testReadJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -63,30 +65,7 @@ public class ReadHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } /** diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java index a2a9ddf740b..ccc5109eb02 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java @@ -6,7 +6,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -14,14 +17,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import javax.annotation.Nonnull; import java.util.List; @@ -38,17 +42,22 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class SearchHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static InstantDt ourReturnPublished; - private static Server ourServer; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -65,7 +74,7 @@ public class SearchHl7OrgDstu2Test { @Test public void testEncodeConvertsReferencesToRelativeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -82,7 +91,7 @@ public class SearchHl7OrgDstu2Test { @Test public void testResultBundleHasUuid() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -97,7 +106,7 @@ public class SearchHl7OrgDstu2Test { ourReturnPublished = new InstantDt("2011-02-03T11:22:33Z"); assertEquals(ourReturnPublished.getValueAsString(), "2011-02-03T11:22:33Z"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithBundleProvider&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithBundleProvider&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -153,7 +162,7 @@ public class SearchHl7OrgDstu2Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } @@ -161,31 +170,7 @@ public class SearchHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java index af3acf25d3d..a288b8217c0 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.HumanName; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -35,12 +29,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static String ourLastMethod; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +52,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,7 +60,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } public static class DummyPatientResourceProvider implements IResourceProvider { @@ -86,33 +87,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java index 009b4656a8b..5269847861a 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java @@ -2,23 +2,18 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.stringContainsInOrder; @@ -26,15 +21,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithHl7OrgDstu2BundleTest { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithHl7OrgDstu2BundleTest.class); - @Test + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -50,10 +52,10 @@ public class SearchWithHl7OrgDstu2BundleTest { "", "" , "", - "", + "", "" , "" , - //"" , + //"" , "" , "")); // @formatter:off @@ -62,30 +64,7 @@ public class SearchWithHl7OrgDstu2BundleTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } /** diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java index a2b44b07ae7..2f202dc09ff 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java @@ -45,8 +45,8 @@ import org.hl7.fhir.dstu2.model.StringType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; import java.util.Collection; import java.util.List; import java.util.Set; @@ -492,7 +492,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { @Operation(name = "plain", idempotent = true, returnParameters= { @OperationParam(min=1, max=2, name="out1", type=StringType.class) }) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -501,7 +501,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java index f101a33165c..89ab146034d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java @@ -16,7 +16,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2.model.Bundle.HTTPVerb; @@ -231,7 +231,7 @@ public class TransactionWithBundleResourceParamHl7OrgDstu2Test { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java index 2c33117196d..f78b2581532 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java @@ -10,8 +10,12 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -22,14 +26,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -40,19 +45,26 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UpdateConditionalHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; private static String ourLastConditionalUrl; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalHl7OrgDstu2Test.class); - private static int ourPort; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static String ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; - - - - @BeforeEach + + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @BeforeEach public void before() { ourLastId = null; ourLastConditionalUrl = null; @@ -66,7 +78,7 @@ public class UpdateConditionalHl7OrgDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -78,7 +90,7 @@ public class UpdateConditionalHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId); assertNull(ourLastIdParam); @@ -93,7 +105,7 @@ public class UpdateConditionalHl7OrgDstu2Test { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -105,7 +117,7 @@ public class UpdateConditionalHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals("Patient/2", new IdType(ourLastId).toUnqualified().getValue()); assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue()); @@ -119,7 +131,7 @@ public class UpdateConditionalHl7OrgDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -138,32 +150,10 @@ public class UpdateConditionalHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java index 268ef8c772d..6994f042f11 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; @@ -18,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.OperationOutcome; import org.hl7.fhir.dstu2.model.Organization; @@ -30,6 +33,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,17 +45,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class ValidateHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; private static EncodingEnum ourLastEncoding; private static String ourLastResourceBody; - private static int ourPort; - private static Server ourServer; private static OperationOutcome ourOutcomeToReturn; private static ValidationModeEnum ourLastMode; private static String ourLastProfile; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - @BeforeEach() + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach() public void before() { ourLastResourceBody = null; ourLastEncoding = null; @@ -72,7 +84,7 @@ public class ValidateHl7OrgDstu2Test { params.addParameter().setName("profile").setValue(new StringType("http://foo")); params.addParameter().setName("mode").setValue(new StringType(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -96,7 +108,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -121,7 +133,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -143,7 +155,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,30 +168,9 @@ public class ValidateHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index b537170eff6..5aa456fa37a 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 - 6.11.4-SNAPSHOT + 6.11.5-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -106,8 +106,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api true @@ -140,6 +140,11 @@ + + com.helger.commons + ph-collection + test + - - com.helger - ph-schematron - test - - - com.helger - ph-commons - test - - - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api - test - - - org.glassfish.jaxb - jaxb-runtime - test - - - net.sf.json-lib diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java index 012552f7896..f7e9bd5797d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java @@ -15,9 +15,6 @@ public class CustomThymeleafNarrativeGeneratorR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomThymeleafNarrativeGeneratorR4Test.class); - /** - * Don't use cached here since we modify the context - */ private final FhirContext myCtx = FhirContext.forR4Cached(); @AfterEach @@ -30,7 +27,6 @@ public class CustomThymeleafNarrativeGeneratorR4Test { */ @Test public void testStandardType() { - CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("classpath:narrative/standardtypes_r4.properties"); myCtx.setNarrativeGenerator(gen); @@ -51,6 +47,7 @@ public class CustomThymeleafNarrativeGeneratorR4Test { @Test public void testCustomType() { + myCtx.setNarrativeGenerator(null); CustomPatient patient = new CustomPatient(); patient.setActive(true); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java index 6a88ca6c9e8..f78bc4d32a3 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java @@ -91,7 +91,7 @@ public class DefaultThymeleafNarrativeGeneratorR4Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java index a27fddac70c..7ddda19896a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java @@ -38,6 +38,8 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Base; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.core.io.Resource; @@ -88,6 +90,7 @@ public class RDFParserTest extends BaseTest { */ @ParameterizedTest @MethodSource("getInputFiles") + @Execution(ExecutionMode.CONCURRENT) public void testRDFRoundTrip(String referenceFilePath) throws IOException { String referenceFileName = referenceFilePath.substring(referenceFilePath.lastIndexOf("/")+1); IBaseResource referenceResource = parseJson(new FileInputStream(referenceFilePath)); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java index e14028ac170..7d5a51910f1 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java @@ -9,17 +9,17 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java index fe78ecb8004..026af8d9059 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java @@ -4,69 +4,66 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.impl.HttpBasicAuthInterceptor; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.Validate; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class ClientIntegrationTest { - private Server myServer; - private MyPatientResourceProvider myPatientProvider; - private static FhirContext ourCtx = FhirContext.forR4(); + private MyPatientResourceProvider myPatientProvider = new MyPatientResourceProvider(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @BeforeEach - public void before() { - myServer = new Server(0); + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(myPatientProvider) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); - myPatientProvider = new MyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(myPatientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - - } + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @SuppressWarnings("deprecation") @Test public void testClientSecurity() throws Exception { - JettyUtil.startServer(myServer); - int myPort = JettyUtil.getPortForStartedServer(myServer); - - FhirContext ctx = FhirContext.forR4(); - HttpClientBuilder builder = HttpClientBuilder.create(); // PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); // builder.setConnectionManager(connectionManager); builder.addInterceptorFirst(new HttpBasicAuthInterceptor("foobar", "boobear")); CloseableHttpClient httpClient = builder.build(); - ctx.getRestfulClientFactory().setHttpClient(httpClient); - PatientClient client = ctx.newRestfulClient(PatientClient.class, "http://localhost:" + myPort + "/"); + ourCtx.getRestfulClientFactory().setHttpClient(httpClient); + + PatientClient client = ourCtx.newRestfulClient(PatientClient.class, ourServer.getBaseUrl() + "/"); List actualPatients = client.searchForPatients(new StringDt("AAAABBBB")); assertEquals(1, actualPatients.size()); @@ -77,7 +74,7 @@ public class ClientIntegrationTest { @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); + ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx)); } public static class MyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java index e24883f5696..31bf708965e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java @@ -5,51 +5,48 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import javax.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; public class CompressOutgoingContentInterceptorTest { - private static IGenericClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Patient ourLastPatient; private static String ourLastReq; private static String ourLastResponseEncoding; - private static int ourPort; - private static Server ourServer; - @BeforeEach - public void before() { - ourClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testCreate() { - ourClient.registerInterceptor(new GZipContentInterceptor()); + IGenericClient client = ourServer.getFhirClient(); + client.registerInterceptor(new GZipContentInterceptor()); Patient p = new Patient(); p.addName().setFamily("FAMILY"); - ourClient.create().resource(p).execute(); + client.create().resource(p).execute(); assertEquals("FAMILY", p.getName().get(0).getFamily()); assertEquals("gzip", ourLastReq); @@ -59,28 +56,9 @@ public class CompressOutgoingContentInterceptorTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Create diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java index e04b623afe2..9231bbe7184 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java @@ -11,17 +11,17 @@ import ca.uhn.fhir.util.TestUtil; import org.apache.commons.collections.EnumerationUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java index 2f4109fb05e..93c67d6a6f9 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java @@ -5,11 +5,16 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; @@ -17,8 +22,8 @@ import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; @@ -26,6 +31,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentMatcher; import org.slf4j.LoggerFactory; @@ -42,11 +48,18 @@ import static org.mockito.Mockito.when; public class LoggingInterceptorTest { private static final FhirContext ourCtx = FhirContext.forR4Cached(); - private static int ourPort; - private static Server ourServer; private Logger myLoggerRoot; private Appender myMockAppender; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @AfterEach public void after() { myLoggerRoot.detachAppender(myMockAppender); @@ -71,7 +84,7 @@ public class LoggingInterceptorTest { @Test public void testLoggerNonVerbose() { System.out.println("Starting testLogger"); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); LoggingInterceptor interceptor = new LoggingInterceptor(false); @@ -87,7 +100,7 @@ public class LoggingInterceptorTest { System.out.println("** Got Message: " + formattedMessage); System.out.flush(); return - formattedMessage.contains("Client request: GET http://localhost:" + ourPort + "/Patient/1 HTTP/1.1") || + formattedMessage.contains("Client request: GET " + ourServer.getBaseUrl() + "/Patient/1 HTTP/1.1") || formattedMessage.contains("Client response: HTTP 200 OK (Patient/1/_history/1) in "); } })); @@ -96,7 +109,7 @@ public class LoggingInterceptorTest { @Test public void testLoggerVerbose() { System.out.println("Starting testLogger"); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); LoggingInterceptor interceptor = new LoggingInterceptor(true); @@ -132,7 +145,7 @@ public class LoggingInterceptorTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(IRestfulClientFactory.DEFAULT_SERVER_VALIDATION_MODE); TestUtil.randomizeLocaleAndTimezone(); } @@ -147,20 +160,6 @@ public class LoggingInterceptorTest { context.reset(); configurator.doConfigure(conf); - ourServer = new Server(0); - - DummyProvider patientProvider = new DummyProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java index aedeb3c7f84..2a6b5016d3e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java @@ -4,32 +4,26 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.DateUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.jena.base.Sys; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +35,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; -import java.util.concurrent.TimeUnit; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; @@ -59,12 +52,8 @@ public class DateRangeParamR4Test { private static final SimpleDateFormat ourFmtLowerForTime; private static final SimpleDateFormat ourFmtUpperForTime; private static final Logger ourLog = LoggerFactory.getLogger(DateRangeParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4Cached(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static DateRangeParam ourLastDateRange; - private static int ourPort; - private static Server ourServer; - private static String ourBaseUrl; static { ourFmtLower = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); @@ -77,6 +66,17 @@ public class DateRangeParamR4Test { ourFmtUpperForTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); } + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourLastDateRange = null; @@ -84,7 +84,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForMultipleUnqualifiedDate() throws Exception { - String baseUrl = "http://localhost:" + ourPort + "/Patient?" + Patient.SP_BIRTHDATE + "="; + String baseUrl = ourServer.getBaseUrl() + "/Patient?" + Patient.SP_BIRTHDATE + "="; HttpGet httpGet = new HttpGet(baseUrl + "2012-01-01&" + Patient.SP_BIRTHDATE + "=2012-02-03"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); @@ -102,7 +102,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneUnqualifiedDate() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,7 +118,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateEq() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=eq2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=eq2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -134,7 +134,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateGt() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=gt2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=gt2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -150,7 +150,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateLt() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=lt2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=lt2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -166,7 +166,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateGe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=ge2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=ge2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -182,7 +182,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateLe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=le2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=le2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -198,7 +198,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateNe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=ne2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=ne2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -212,7 +212,7 @@ public class DateRangeParamR4Test { @Test public void testRangeWithDatePrecision() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=gt2012-01-01&birthdate=lt2012-01-03"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=gt2012-01-01&birthdate=lt2012-01-03"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -412,7 +412,7 @@ public class DateRangeParamR4Test { assertEquals(new DateRangeParam(null, upperBound), new DateRangeParam(null, new DateParam(LESSTHAN_OR_EQUALS, upperBound))); } - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { @@ -466,33 +466,7 @@ public class DateRangeParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourBaseUrl = "http://localhost:" + ourPort + "/Patient"; - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java index a1bc70a2618..3f73d339ed0 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java @@ -2,43 +2,42 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.RegisterExtension; public class BaseR4ServerTest { - protected FhirContext myCtx = FhirContext.forR4Cached(); - private Server myServer; - protected IGenericClient myClient; - protected String myBaseUrl; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PlaceholderProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + public HttpClientExtension ourClient = new HttpClientExtension(); protected void startServer(Object theProvider) throws Exception { - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(theProvider); - ServletHandler proxyHandler = new ServletHandler(); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - - myServer = new Server(0); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - int port = JettyUtil.getPortForStartedServer(myServer); - - myBaseUrl = "http://localhost:" + port; - myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - myClient = myCtx.newRestfulGenericClient(myBaseUrl); + ourServer.getRestfulServer().registerProvider(theProvider); } + private static class PlaceholderProvider { + + @Operation(name = "placeholderOperation") + public void placeholderOperation() { + // nothing + } + + } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java index c507eb82aca..13f98b36727 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java @@ -7,8 +7,10 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -18,39 +20,38 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Binary; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -public class BinaryServerR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); +public class BinaryServerR4Test { + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Binary ourLastBinary; private static byte[] ourLastBinaryBytes; private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; private static IdType ourLastId; private static Binary ourNextBinary; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new BinaryProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastBinary = null; @@ -69,7 +70,7 @@ public class BinaryServerR4Test { ourNextBinary.setSecurityContext(new Reference("Patient/1")); ourNextBinary.setContentType("application/foo"); - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Binary/A"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Binary/A"); get.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(get); try { @@ -77,7 +78,7 @@ public class BinaryServerR4Test { assertEquals("application/foo", status.getEntity().getContentType().getValue()); assertEquals("Patient/1", status.getFirstHeader(Constants.HEADER_X_SECURITY_CONTEXT).getValue()); assertEquals("W/\"222\"", status.getFirstHeader(Constants.HEADER_ETAG).getValue()); - assertEquals("http://localhost:" + ourPort + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); + assertEquals(ourServer.getBaseUrl() + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); byte[] content = IOUtils.toByteArray(status.getEntity().getContent()); @@ -97,7 +98,7 @@ public class BinaryServerR4Test { ourNextBinary.setSecurityContext(new Reference("Patient/1")); ourNextBinary.setContentType("application/foo"); - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Binary/A"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Binary/A"); get.addHeader("Content-Type", "application/foo"); get.addHeader("Accept", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(get); @@ -106,7 +107,7 @@ public class BinaryServerR4Test { assertEquals("application/json+fhir;charset=utf-8", status.getEntity().getContentType().getValue()); assertEquals("Patient/1", status.getFirstHeader(Constants.HEADER_X_SECURITY_CONTEXT).getValue()); assertEquals("W/\"222\"", status.getFirstHeader(Constants.HEADER_ETAG).getValue()); - assertEquals("http://localhost:" + ourPort + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); + assertEquals(ourServer.getBaseUrl() + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); String content = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -118,7 +119,7 @@ public class BinaryServerR4Test { @Test public void testPostBinaryWithSecurityContext() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); post.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, "Encounter/2"); @@ -136,7 +137,7 @@ public class BinaryServerR4Test { @Test public void testPostRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(post); @@ -161,7 +162,7 @@ public class BinaryServerR4Test { b.setContent(new byte[]{0, 1, 2, 3, 4}); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -184,7 +185,7 @@ public class BinaryServerR4Test { b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -200,7 +201,7 @@ public class BinaryServerR4Test { @Test public void testPostRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); CloseableHttpResponse status = ourClient.execute(post); try { @@ -213,7 +214,7 @@ public class BinaryServerR4Test { @Test public void testPutBinaryWithSecurityContext() throws Exception { - HttpPut post = new HttpPut("http://localhost:" + ourPort + "/Binary/A"); + HttpPut post = new HttpPut(ourServer.getBaseUrl() + "/Binary/A"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); post.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, "Encounter/2"); @@ -232,31 +233,9 @@ public class BinaryServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - public static class BinaryProvider implements IResourceProvider { @Create() public MethodOutcome createBinary(@ResourceParam Binary theBinary, @ResourceParam String theBinaryString, @ResourceParam byte[] theBinaryBytes) { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java index d10efd46300..0e82c283a81 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java @@ -13,7 +13,7 @@ import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java index d9a7957056c..9cf95713f2d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java @@ -8,10 +8,13 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -24,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.IdType; @@ -37,6 +40,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -54,17 +58,21 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class CreateR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateR4Test.class); public static OperationOutcome ourReturnOo; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @AfterEach - public void after() { - ourServlet.setDefaultPreferReturn(RestfulServer.DEFAULT_PREFER_RETURN); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProviderCreate()) + .registerProvider(new PatientProviderRead()) + .registerProvider(new PatientProviderSearch()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s->s.setDefaultPreferReturn(RestfulServer.DEFAULT_PREFER_RETURN)) + .setDefaultPrettyPrint(false); + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOo = null; @@ -73,7 +81,7 @@ public class CreateR4Test { @Test public void testCreateIgnoresIdInResourceBody() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"id\":\"999\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -86,14 +94,14 @@ public class CreateR4Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @Test public void testCreateFailsIfNoContentTypeProvided() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"id\":\"999\", \"status\":\"active\"}", (ContentType) null)); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { @@ -113,7 +121,7 @@ public class CreateR4Test { @Test public void testCreateReturnsLocationHeader() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -126,7 +134,7 @@ public class CreateR4Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @@ -134,7 +142,7 @@ public class CreateR4Test { public void testCreateReturnsOperationOutcome() throws Exception { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -154,7 +162,7 @@ public class CreateR4Test { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"gender\":\"male\"}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -170,7 +178,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent1() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -189,7 +197,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent2() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -208,7 +216,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent3() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -229,7 +237,7 @@ public class CreateR4Test { @Test public void testCreateWithInvalidContent() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -256,7 +264,7 @@ public class CreateR4Test { String body = ourCtx.newJsonParser().encodeResourceToString(p); HttpPost httpPost; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { @@ -279,9 +287,9 @@ public class CreateR4Test { p.setActive(true); String body = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); - ourServlet.setDefaultPreferReturn(PreferReturnEnum.OPERATION_OUTCOME); + ourServer.getRestfulServer().setDefaultPreferReturn(PreferReturnEnum.OPERATION_OUTCOME); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -304,9 +312,9 @@ public class CreateR4Test { p.setActive(true); String body = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); - ourServlet.setDefaultPreferReturn(PreferReturnEnum.MINIMAL); + ourServer.getRestfulServer().setDefaultPreferReturn(PreferReturnEnum.MINIMAL); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -321,7 +329,7 @@ public class CreateR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -389,7 +397,7 @@ public class CreateR4Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); @@ -408,33 +416,7 @@ public class CreateR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProviderCreate patientProviderCreate = new PatientProviderCreate(); - PatientProviderRead patientProviderRead = new PatientProviderRead(); - PatientProviderSearch patientProviderSearch = new PatientProviderSearch(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.setResourceProviders(patientProviderCreate, patientProviderRead, patientProviderSearch); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java index edbbb0b25be..6c26e113fe2 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java @@ -4,17 +4,20 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; @@ -22,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,16 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class DeleteConditionalR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static IGenericClient ourHapiClient; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static String ourLastConditionalUrl; private static IdType ourLastIdParam; private static boolean ourLastRequestWasDelete; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastConditionalUrl = null; @@ -54,7 +63,8 @@ public class DeleteConditionalR4Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - ourHapiClient + ourServer + .getFhirClient() .delete() .resourceConditionalByType(Patient.class) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS", "SOMEID")).execute(); @@ -84,33 +94,7 @@ public class DeleteConditionalR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java index d4f2aea8004..c4c867f8f10 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.Header; @@ -20,39 +22,37 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Date; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class ETagServerR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Date ourLastModifiedDate; - private static int ourPort; - private static Server ourServer; - private static PoolingHttpClientConnectionManager ourConnectionManager; private static IdType ourLastId; private static boolean ourPutVersionInPatientId; private static boolean ourPutVersionInPatientMeta; - @BeforeEach + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourLastId = null; ourPutVersionInPatientId = true; @@ -74,7 +74,7 @@ public class ETagServerR4Test { private void doTestAutomaticNotModified() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_NONE_MATCH, "\"222\""); HttpResponse status = ourClient.execute(httpGet); assertEquals(Constants.STATUS_HTTP_304_NOT_MODIFIED, status.getStatusLine().getStatusCode()); @@ -84,7 +84,7 @@ public class ETagServerR4Test { public void testETagHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -105,7 +105,7 @@ public class ETagServerR4Test { ourPutVersionInPatientId = false; ourLastModifiedDate = null; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -120,7 +120,7 @@ public class ETagServerR4Test { public void testLastModifiedHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -143,7 +143,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"221\""); CloseableHttpResponse status = ourClient.execute(http); @@ -161,7 +161,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"222\""); CloseableHttpResponse status = ourClient.execute(http); @@ -178,7 +178,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(http); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -187,32 +187,10 @@ public class ETagServerR4Test { } @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + public static void afterClass() { + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourConnectionManager = new PoolingHttpClientConnectionManager(50000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(ourConnectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java index b4a6e2b015a..9f888145b1e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -18,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DiagnosticReport; @@ -35,6 +38,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Collection; @@ -51,27 +55,34 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ElementsParamR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElementsParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Set ourLastElements; - private static int ourPort; - private static Server ourServer; private static Procedure ourNextProcedure; - private static RestfulServer ourServlet; private static Observation ourNextObservation; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyProcedureResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastElements = null; ourNextProcedure = null; - ourServlet.setElementsSupport(new RestfulServer().getElementsSupport()); + ourServer.getRestfulServer().setElementsSupport(new RestfulServer().getElementsSupport()); } @Test public void testElementsOnChoiceWithGenericName() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=value,status", + ourServer.getBaseUrl() + "/Observation?_elements=value,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -85,7 +96,7 @@ public class ElementsParamR4Test { public void testElementsOnChoiceWithSpecificName() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=valueQuantity,status", + ourServer.getBaseUrl() + "/Observation?_elements=valueQuantity,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -100,7 +111,7 @@ public class ElementsParamR4Test { public void testElementsOnChoiceWithSpecificNameNotMatching() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=valueString,status", + ourServer.getBaseUrl() + "/Observation?_elements=valueString,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -113,7 +124,7 @@ public class ElementsParamR4Test { public void testExcludeResources() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements:exclude=Procedure,DiagnosticReport,*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements:exclude=Procedure,DiagnosticReport,*.meta", bundle -> { assertEquals(null, bundle.getEntry().get(0).getResource()); assertEquals(null, bundle.getEntry().get(1).getResource()); @@ -133,7 +144,7 @@ public class ElementsParamR4Test { HttpGet httpGet; encodingEnum = EncodingEnum.JSON; - httpGet = new HttpGet(("http://localhost:" + ourPort + "/Procedure?_include=*&_elements=DiagnosticReport:foo") + "&_pretty=true&_format=" + encodingEnum.getFormatContentType()); + httpGet = new HttpGet((ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=DiagnosticReport:foo") + "&_pretty=true&_format=" + encodingEnum.getFormatContentType()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -156,7 +167,7 @@ public class ElementsParamR4Test { @Test public void testReadSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_elements=name,maritalStatus", + ourServer.getBaseUrl() + "/Patient/1?_elements=name,maritalStatus", Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -173,7 +184,7 @@ public class ElementsParamR4Test { @Test public void testReadSummaryTrue() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_elements=name", + ourServer.getBaseUrl() + "/Patient/1?_elements=name", Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -188,7 +199,7 @@ public class ElementsParamR4Test { @Test public void testSearchSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient?_elements=name,maritalStatus", + ourServer.getBaseUrl() + "/Patient?_elements=name,maritalStatus", bundle -> { assertEquals("1", bundle.getTotalElement().getValueAsString()); String responseContent = ourCtx.newXmlParser().encodeResourceToString(bundle.getEntry().get(0).getResource()); @@ -204,7 +215,7 @@ public class ElementsParamR4Test { @Test public void testSearchSummaryText() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient?_elements=text&_pretty=true", + ourServer.getBaseUrl() + "/Patient?_elements=text&_pretty=true", bundle -> { assertEquals("1", bundle.getTotalElement().getValueAsString()); String responseContent = ourCtx.newXmlParser().encodeResourceToString(bundle.getEntry().get(0).getResource()); @@ -224,7 +235,7 @@ public class ElementsParamR4Test { public void testStandardElementsFilter() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=reasonCode,status", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=reasonCode,status", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -246,7 +257,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilter() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -272,7 +283,7 @@ public class ElementsParamR4Test { .setUrl("http://quantity") .setValue(Quantity.fromUcum("1.1", "mg")); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -282,7 +293,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension.value", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -292,7 +303,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension.value.value", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension.value.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -302,7 +313,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.reason", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.reason", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -314,7 +325,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilterWithMetadataExcluded() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -340,7 +351,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilterDoesntAffectFocalResource() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Observation.subject", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Observation.subject", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -362,10 +373,10 @@ public class ElementsParamR4Test { @Test public void testMultiResourceElementsFilterWithMetadataExcludedStandardMode() throws IOException { - ourServlet.setElementsSupport(ElementsSupportEnum.STANDARD); + ourServer.getRestfulServer().setElementsSupport(ElementsSupportEnum.STANDARD); createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -388,7 +399,7 @@ public class ElementsParamR4Test { public void testElementsFilterWithComplexPath() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.reasonCode.coding.code", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.reasonCode.coding.code", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -507,32 +518,8 @@ public class ElementsParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.registerProvider(new DummyPatientResourceProvider()); - ourServlet.registerProvider(new DummyProcedureResourceProvider()); - ourServlet.registerProvider(new DummyObservationResourceProvider()); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java index 6764e39cee8..d208c883aa8 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java @@ -9,7 +9,9 @@ import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -19,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DateTimeType; @@ -32,6 +34,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,15 +50,22 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class HistoryR4Test { private static final Logger ourLog = LoggerFactory.getLogger(HistoryR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static DateRangeParam ourLastAt; private static InstantType ourLastSince; private static IPrimitiveType ourLastSince2; private static IPrimitiveType ourLastSince3; private static IPrimitiveType ourLastSince4; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPlainProvider()) + .registerProvider(new DummyResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -69,7 +79,7 @@ public class HistoryR4Test { @Test public void testAt() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_at=gt2001&_at=lt2005"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history?_at=gt2001&_at=lt2005"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -86,7 +96,7 @@ public class HistoryR4Test { @Test public void testInstanceHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history?_pretty=true"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -96,8 +106,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -105,7 +115,7 @@ public class HistoryR4Test { @Test public void testServerHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -115,8 +125,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -124,7 +134,7 @@ public class HistoryR4Test { @Test public void testSince() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_since=2005"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history?_since=2005"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -146,7 +156,7 @@ public class HistoryR4Test { @Test public void testTypeHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/_history"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -158,8 +168,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -170,7 +180,7 @@ public class HistoryR4Test { @Test public void testVread() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/456"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -185,34 +195,9 @@ public class HistoryR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPlainProvider plainProvider = new DummyPlainProvider(); - DummyResourceProvider patientProvider = new DummyResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.registerProviders(plainProvider, patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPlainProvider { @History diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java index 247c425c19a..06143b2dafe 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java @@ -12,7 +12,9 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.TestUtil; @@ -23,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DiagnosticReport; import org.hl7.fhir.r4.model.Observation; @@ -35,6 +37,7 @@ import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -53,14 +56,22 @@ import static org.junit.jupiter.api.Assertions.fail; public class IncludeTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyDiagnosticReportResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(400, status.getStatusLine().getStatusCode()); String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -72,7 +83,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -98,7 +109,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -124,7 +135,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -149,7 +160,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -205,7 +216,7 @@ public class IncludeTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -221,7 +232,7 @@ public class IncludeTest { @Test public void testOneInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -238,7 +249,7 @@ public class IncludeTest { @Test public void testOneIncludeIterate() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&" + Constants.PARAM_INCLUDE_ITERATE + "=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&" + Constants.PARAM_INCLUDE_ITERATE + "=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -255,7 +266,7 @@ public class IncludeTest { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -277,7 +288,7 @@ public class IncludeTest { @Test public void testStringInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=stringInclude&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=stringInclude&_include=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -478,34 +489,7 @@ public class IncludeTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forR4(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java index 0a52c202083..899f97e383e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java @@ -40,9 +40,10 @@ public class LastNProviderTest extends BaseR4ServerTest { MyProvider provider = new MyProvider(); startServer(provider); - Bundle response = myClient + Bundle response = ourServer + .getFhirClient() .search() - .byUrl(myBaseUrl + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") + .byUrl(ourServer.getBaseUrl() + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") .returnBundle(Bundle.class) .execute(); assertEquals("abc123", response.getIdElement().getIdPart()); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java index b73b66cdf3e..07aad245a88 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java @@ -8,30 +8,24 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -41,14 +35,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MultitenancyR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MultitenancyR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static TokenAndListParam ourIdentifiers; private static String ourLastMethod; - private static int ourPort; - private static Server ourServer; private static String ourLastTenantId; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s -> s.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy())) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -58,24 +60,8 @@ public class MultitenancyR4Test { @Test public void testUrlBaseStrategy() throws Exception { - ourServer = new Server(0); - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANT2/Patient?identifier=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/TENANT2/Patient?identifier=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -88,22 +74,22 @@ public class MultitenancyR4Test { Bundle resp = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent); ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - assertEquals("http://localhost:" + ourPort + "/TENANT2/Patient?identifier=foo%7Cbar", resp.getLink("self").getUrl()); - assertEquals("http://localhost:" + ourPort + "/TENANT2/Patient/0", resp.getEntry().get(0).getFullUrl()); - assertEquals("http://localhost:"+ourPort+"/TENANT2/Patient/0", resp.getEntry().get(0).getResource().getId()); - assertThat(resp.getLink("next").getUrl(), startsWith("http://localhost:"+ourPort+"/TENANT2?_getpages=")); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient?identifier=foo%7Cbar", resp.getLink("self").getUrl()); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient/0", resp.getEntry().get(0).getFullUrl()); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient/0", resp.getEntry().get(0).getResource().getId()); + assertThat(resp.getLink("next").getUrl(), startsWith(ourServer.getBaseUrl() + "/TENANT2?_getpages=")); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } // GET the root - httpGet = new HttpGet("http://localhost:" + ourPort + "/"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/"); status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(400, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"diagnostics\":\""+ Msg.code(307) + "This is the base URL of a multitenant FHIR server. Unable to handle this request, as it does not contain a tenant ID.\"")); + assertThat(responseContent, containsString("\"diagnostics\":\"" + Msg.code(307) + "This is the base URL of a multitenant FHIR server. Unable to handle this request, as it does not contain a tenant ID.\"")); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } @@ -111,20 +97,11 @@ public class MultitenancyR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() { - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -135,8 +112,8 @@ public class MultitenancyR4Test { @SuppressWarnings("rawtypes") @Search() public List search( - RequestDetails theRequestDetails, - @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { + RequestDetails theRequestDetails, + @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { ourLastMethod = "search"; ourIdentifiers = theIdentifiers; ourLastTenantId = theRequestDetails.getTenantId(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java index 125754b1f4a..faeb24863a6 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java @@ -9,7 +9,11 @@ import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -21,14 +25,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.MolecularSequence; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; @@ -39,7 +44,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,15 +58,23 @@ import static org.junit.jupiter.api.Assertions.fail; public class OperationGenericServer2R4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServer2R4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IdType ourLastId; private static Object ourLastParam1; private static Object ourLastParam2; private static Object ourLastParam3; private static Parameters ourLastResourceParam; - private int myPort; - private Server myServer; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new HashMapResourceProvider<>(ourCtx, MolecularSequence.class)) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -103,14 +118,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val")); p.addParameter().setName("PARAM2").setValue(new Coding("sys", "val", "dis")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -159,14 +174,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val")); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -218,14 +233,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new UriType("PARAM1val")); p.addParameter().setName("PARAM2").setValue(new StringType("PARAM2val")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -264,11 +279,11 @@ public class OperationGenericServer2R4Test { try { PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { ConfigurationException ce = (ConfigurationException) e.getCause(); - assertThat(ce.getMessage(), containsString("Failure scanning class PatientProvider: " + Msg.code(405) + "Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test")); + assertThat(ce.getMessage(), containsString(Msg.code(405) + "Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test")); } } @@ -295,9 +310,9 @@ public class OperationGenericServer2R4Test { } PlainProvider provider = new PlainProvider(); - startServer(provider); + ourServer.registerProvider(provider); - HttpGet httpPost = new HttpGet("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(response); @@ -324,37 +339,19 @@ public class OperationGenericServer2R4Test { PlainProvider provider = new PlainProvider(); try { - startServer(provider); + ourServer.registerProvider(provider); fail(); - } catch (ServletException e) { - Throwable cause = e.getRootCause(); - assertEquals(Msg.code(288) + "Failure scanning class PlainProvider: " + Msg.code(423) + "Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - " + Msg.code(1684) + "Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage()); + } catch (ConfigurationException e) { + Throwable cause = e.getCause(); + assertEquals(Msg.code(423) + "Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - " + Msg.code(1684) + "Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage()); } } - private void startServer(Object theProvider) throws Exception { - myServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.registerProvider(theProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); - } - @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); + TestUtil.randomizeLocaleAndTimezone(); } @AfterAll @@ -362,15 +359,4 @@ public class OperationGenericServer2R4Test { TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() { - ourCtx = FhirContext.forR4(); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java index 999e7ffc1ae..56e17ae4a0f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -20,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; @@ -31,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -42,16 +45,23 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class OperationGenericServerR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServerR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IdType ourLastId; private static String ourLastMethod; private static StringType ourLastParam1; private static Patient ourLastParam2; - private static int ourPort; - private static Server ourServer; private static Parameters ourLastResourceParam; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastParam1 = null; @@ -69,7 +79,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -99,7 +109,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -126,7 +136,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -150,7 +160,7 @@ public class OperationGenericServerR4Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -174,7 +184,7 @@ public class OperationGenericServerR4Test { @Test public void testSearchGetsClassifiedAppropriately() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpPost); try { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -275,35 +285,8 @@ public class OperationGenericServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forR4(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java index 3215597a114..20744e7ca0f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java @@ -36,8 +36,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java index aed0f118745..f3b35670e85 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java @@ -49,7 +49,7 @@ import static org.mockito.Mockito.when; */ public class PagingTest { - private FhirContext ourContext = FhirContext.forR4(); + private FhirContext ourContext = FhirContext.forR4Cached(); @RegisterExtension public RestfulServerExtension myServerExtension = new RestfulServerExtension(ourContext); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java index 6a05c7f0ff2..ce11063307e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -17,8 +20,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; @@ -26,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -47,20 +51,25 @@ import static org.mockito.Mockito.when; public class PagingUsingNamedPagesR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PagingUsingNamedPagesR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IBundleProvider ourNextBundleProvider; private IPagingProvider myPagingProvider; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { myPagingProvider = mock(IPagingProvider.class); when(myPagingProvider.canStoreSearchResults()).thenReturn(true); - servlet.setPagingProvider(myPagingProvider); + ourServer.getRestfulServer().setPagingProvider(myPagingProvider); ourNextBundleProvider = null; } @@ -118,32 +127,32 @@ public class PagingUsingNamedPagesR4Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "/Patient?_format=xml", linkSelf); + assertEquals(ourServer.getBaseUrl() + "/Patient?_format=xml", linkSelf); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkNext); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkNext); assertNull(bundle.getLink(Constants.LINK_PREVIOUS)); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkSelf); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkSelf); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkNext); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkNext); linkPrev = bundle.getLink(Constants.LINK_PREVIOUS).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=searchset", linkPrev); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=searchset", linkPrev); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkSelf); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkSelf); assertNull(bundle.getLink(Constants.LINK_NEXT)); linkPrev = bundle.getLink(Constants.LINK_PREVIOUS).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkPrev); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkPrev); } @Test @@ -153,7 +162,7 @@ public class PagingUsingNamedPagesR4Test { when(myPagingProvider.retrieveResultList(any(), nullable(String.class), nullable(String.class))).thenReturn(null); // With ID - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -162,7 +171,7 @@ public class PagingUsingNamedPagesR4Test { } // Without ID - httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -181,7 +190,7 @@ public class PagingUsingNamedPagesR4Test { when(myPagingProvider.retrieveResultList(any(), eq("SEARCHID0"), eq("PAGEID0"))).thenReturn(provider0); // Initial search - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -197,37 +206,10 @@ public class PagingUsingNamedPagesR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - builder.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(600000).build()); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java index dd278c04bc9..80c9a1f6484 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java @@ -29,7 +29,7 @@ public class PatientResourceProvider implements IResourceProvider @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java index 1a11ebd4cfd..72599912728 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java @@ -10,7 +10,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.IsEqual; import org.hamcrest.core.StringStartsWith; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -31,6 +34,7 @@ import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.InstantType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Resource; @@ -38,6 +42,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.HashMap; @@ -51,47 +56,26 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class PlainProviderR4Test { - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PlainProviderR4Test.class); - private CloseableHttpClient myClient; - private int myPort; - private RestfulServer myRestfulServer; - private Server myServer; - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new HashMapResourceProvider<>(ourCtx, Observation.class)) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServletPath("/fhir/context/*"); - @BeforeEach - public void before() throws Exception { - myServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - ServletHolder servletHolder = new ServletHolder(); - myRestfulServer = new RestfulServer(ourCtx); - myRestfulServer.setDefaultResponseEncoding(EncodingEnum.XML); - servletHolder.setServlet(myRestfulServer); - proxyHandler.addServletWithMapping(servletHolder, "/fhir/context/*"); - myServer.setHandler(proxyHandler); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - - builder.setConnectionManager(connectionManager); - myClient = builder.build(); - - } + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testGlobalHistory() throws Exception { GlobalHistoryProvider provider = new GlobalHistoryProvider(); - myRestfulServer.setProviders(provider); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(provider); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; - HttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12")); + String baseUri = ourServer.getBaseUrl(); + HttpResponse status = ourClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12")); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -103,7 +87,7 @@ public class PlainProviderR4Test { assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02")); assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12")); - status = myClient.execute(new HttpGet(baseUri + "/_history?&_count=12")); + status = ourClient.execute(new HttpGet(baseUri + "/_history?&_count=12")); responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -112,7 +96,7 @@ public class PlainProviderR4Test { assertNull(provider.myLastSince); assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12")); - status =myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02")); + status =ourClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02")); responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -125,12 +109,10 @@ public class PlainProviderR4Test { @Test public void testGlobalHistoryNoParams() throws Exception { GlobalHistoryProvider provider = new GlobalHistoryProvider(); - myRestfulServer.setProviders(provider); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(provider); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; - CloseableHttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history")); + String baseUri = ourServer.getBaseUrl(); + CloseableHttpResponse status = ourClient.execute(new HttpGet(baseUri + "/_history")); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -143,14 +125,12 @@ public class PlainProviderR4Test { @Test public void testSearchByParamIdentifier() throws Exception { - myRestfulServer.setProviders(new SearchProvider()); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(new SearchProvider()); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; + String baseUri = ourServer.getBaseUrl(); String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001"; HttpGet httpGet = new HttpGet(uri); - try (CloseableHttpResponse status = myClient.execute(httpGet)) { + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java index 45891b4d569..a412d83006a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java @@ -7,8 +7,11 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; @@ -30,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,14 +45,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class PreferTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferTest.class); - private static int ourPort; - private static Server ourServer; private static IBaseOperationOutcome ourReturnOperationOutcome; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOperationOutcome = null; @@ -64,7 +75,7 @@ public class PreferTest { oo.addIssue().setDiagnostics("DIAG"); ourReturnOperationOutcome = oo; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -79,8 +90,8 @@ public class PreferTest { assertThat(responseContent, is(emptyOrNullString())); // assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), not(containsString("fhir"))); assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE)); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -94,7 +105,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -108,8 +119,8 @@ public class PreferTest { assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("DIAG")); assertEquals("application/xml+fhir;charset=utf-8", status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue().toLowerCase().replace(" ", "")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -119,7 +130,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -133,8 +144,8 @@ public class PreferTest { assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode()); assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), containsString(Constants.CT_FHIR_XML)); assertEquals("", responseContent); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -144,7 +155,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -155,38 +166,16 @@ public class PreferTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java index c4526ff9ee6..06be8295137 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java @@ -15,7 +15,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.ServletRequest; +import jakarta.servlet.ServletRequest; import static ca.uhn.fhir.rest.api.Constants.STATUS_HTTP_202_ACCEPTED; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java index 7624f4c23d1..d7fdff76d8d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java @@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.io.Serializable; import java.util.List; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java index 9034759aa55..77defe6e41c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java @@ -2,10 +2,14 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; import ca.uhn.fhir.rest.server.method.ResponsePage; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -15,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; @@ -25,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -42,15 +47,20 @@ import static org.mockito.Mockito.when; public class SearchBundleProviderWithNoSizeR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static TokenAndListParam ourIdentifiers; private static IBundleProvider ourLastBundleProvider; private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeR4Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -86,7 +96,7 @@ public class SearchBundleProviderWithNoSizeR4Test { BundleLinkComponent linkNext; try { - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -146,33 +156,9 @@ public class SearchBundleProviderWithNoSizeR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java index 3235d7342e4..31eeb8243fd 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java @@ -3,10 +3,13 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.HasAndListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -16,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; @@ -25,6 +28,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -34,14 +38,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchHasParamR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamR4Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static HasAndListParam ourLastParam; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -50,7 +60,7 @@ public class SearchHasParamR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_has:Encounter:patient:type=SURG"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -67,34 +77,9 @@ public class SearchHasParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java index 198285c87e5..4bf2de0a10e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java @@ -38,6 +38,7 @@ import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -81,6 +82,11 @@ public class SearchR4Test { myCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); } + @AfterEach + public void after() { + myCtx.setNarrativeGenerator(null); + } + private Bundle executeSearchAndValidateHasLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException { Bundle bundle = executeSearch(httpGet, theExpectEncoding); String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java index 2698f569afb..7f670af8b28 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java @@ -20,7 +20,9 @@ import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; @@ -38,17 +40,19 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.BaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.Charset; import java.util.ArrayList; @@ -72,27 +76,39 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class SearchSearchServerR4Test { - private static CloseableHttpClient ourClient; - private static final FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IServerAddressStrategy ourDefaultAddressStrategy; private static StringAndListParam ourLastAndList; private static Set ourLastIncludes; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSearchServerR4Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - @BeforeEach + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { - ourServlet.setServerAddressStrategy(ourDefaultAddressStrategy); - ourLastIncludes = null; + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourLastIncludes = null; ourLastAndList = null; + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + } + + @AfterEach + public void after() { + ourCtx.setNarrativeGenerator(null); } @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -110,7 +126,7 @@ public class SearchSearchServerR4Test { @Test public void testGetPagesWithPost() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl()); List parameters = Collections.singletonList(new BasicNameValuePair("_getpages", "AAA")); httpPost.setEntity(new UrlEncodedFormEntity(parameters)); @@ -124,7 +140,7 @@ public class SearchSearchServerR4Test { @Test public void testOmitEmptyOptionalParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id="); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id="); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -139,8 +155,7 @@ public class SearchSearchServerR4Test { @Test public void testParseEscapedValues() throws Exception { - String b = "http://localhost:" + - ourPort + + String b = ourServer.getBaseUrl() + "/Patient?" + escapeUrlParam("findPatientWithAndList") + '=' + escapeUrlParam("NE\\,NE,NE\\,NE") + '&' + escapeUrlParam("findPatientWithAndList") + '=' + escapeUrlParam("NE\\\\NE") + '&' + @@ -169,7 +184,7 @@ public class SearchSearchServerR4Test { @Test public void testReturnLinks() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findWithLinks"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findWithLinks"); CloseableHttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -188,9 +203,9 @@ public class SearchSearchServerR4Test { */ @Test public void testReturnLinksWithAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("https://blah.com/base")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("https://blah.com/base")); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findWithLinks"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findWithLinks"); CloseableHttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -214,7 +229,7 @@ public class SearchSearchServerR4Test { * Load the second page */ String urlPart = linkNext.substring(linkNext.indexOf('?')); - String link = "http://localhost:" + ourPort + urlPart; + String link = ourServer.getBaseUrl() + urlPart; httpGet = new HttpGet(link); status = ourClient.execute(httpGet); @@ -235,7 +250,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchById() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -249,7 +264,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchByIdUsingClient() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); Bundle bundle = client .search() @@ -265,7 +280,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchByPost() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); // add parameters to the post method List parameters = new ArrayList<>(); @@ -290,7 +305,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithInvalidPostUrl() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // should end with // _search // add parameters to the post method @@ -314,7 +329,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithMissingContentType() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // should end with // _search HttpEntity sendentity = new ByteArrayEntity(new byte[] { 1, 2, 3, 4 }); @@ -333,7 +348,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithParamsInBodyAndUrl() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?name=Central"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?name=Central"); // add parameters to the post method List parameters = new ArrayList<>(); @@ -359,7 +374,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchCompartment() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/fooCompartment"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); ourLog.info(responseContent); @@ -375,7 +390,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchGetWithUnderscoreSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/_search?subject%3APatient=100&name=3141-9%2C8302-2%2C8287-5%2C39156-5"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Observation/_search?subject%3APatient=100&name=3141-9%2C8302-2%2C8287-5%2C39156-5"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -395,7 +410,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludes&_include=foo&_include:recurse=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludes&_include=foo&_include:recurse=bar"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -408,7 +423,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersIncludesList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludesList&_include=foo&_include:recurse=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludesList&_include=foo&_include:recurse=bar"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -421,7 +436,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersNone() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludes"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludes"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -433,7 +448,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchWithOrList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?findPatientWithOrList=aaa,bbb"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?findPatientWithOrList=aaa,bbb"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -449,7 +464,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchWithTokenParameter() throws Exception { String token = UrlUtil.escapeUrlParam("http://www.dmix.gov/vista/2957|301"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?tokenParam=" + token); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?tokenParam=" + token); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -464,7 +479,7 @@ public class SearchSearchServerR4Test { @Test public void testSpecificallyNamedQueryGetsPrecedence() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?AAA=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?AAA=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -478,7 +493,7 @@ public class SearchSearchServerR4Test { // Now the named query - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findPatientByAAA&AAA=123"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findPatientByAAA&AAA=123"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -493,37 +508,9 @@ public class SearchSearchServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)); - - ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider()); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourDefaultAddressStrategy = ourServlet.getServerAddressStrategy(); - } - public static class DummyObservationResourceProvider implements IResourceProvider { @Override @@ -675,7 +662,7 @@ public class SearchSearchServerR4Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java index de38b5e2900..a770721dd91 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java @@ -3,9 +3,12 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; @@ -23,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -32,14 +36,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchSortR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortR4Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -48,7 +59,7 @@ public class SearchSortR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_sort=param1,-param2,param3,-param4"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,35 +87,10 @@ public class SearchSortR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java index 45ed11d0b3e..ec77302fa1c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java @@ -24,10 +24,10 @@ import org.slf4j.LoggerFactory; import org.springframework.util.Assert; import javax.annotation.Nonnull; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java index e76dbb0c678..3c095277e1b 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.rest.server; +import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; @@ -16,6 +17,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.test.utilities.server.MockServletUtil; import com.google.common.collect.Lists; +import jakarta.servlet.ServletException; import org.hamcrest.core.StringContains; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.DateType; @@ -24,8 +26,7 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; @@ -39,7 +40,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new UpdateWithWrongConditionalUrlType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString( "Parameters annotated with @ConditionalUrlParam must be of type String, found incorrect parameter in method \"public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$UpdateWithWrongConditionalUrlType.update(ca.uhn.fhir.rest.param.TokenParam,org.hl7.fhir.r4.model.Patient)")); @@ -51,7 +52,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new UpdateWithWrongResourceType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains .containsString("Method 'update' is annotated with @ResourceParam but has a type that is not an implementation of org.hl7.fhir.instance.model.api.IBaseResource or String or byte[]")); @@ -63,7 +64,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new ValidateWithWrongModeType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Mode must be of type ca.uhn.fhir.rest.api.ValidationModeEnum")); } @@ -74,7 +75,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new ValidateWithWrongProfileType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Profile must be of type java.lang.String")); } @@ -94,8 +95,8 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new MyProvider()); fail(); - } catch (ServletException e) { - assertThat(e.getCause().toString(), StringContains.containsString(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(421) + "Illegal method parameter annotation @OptionalParam on method: public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$1MyProvider.update(org.hl7.fhir.r4.model.StringType)")); + } catch (ConfigurationException e) { + assertThat(e.toString(), StringContains.containsString(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(421) + "Illegal method parameter annotation @OptionalParam on method: public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$1MyProvider.update(org.hl7.fhir.r4.model.StringType)")); } } @@ -121,8 +122,8 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(provider); fail(); - } catch (ServletException e) { - assertEquals(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(404) + "@OperationParam detected on method that is not annotated with @Operation: public java.util.List ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$2MyProvider.search(org.hl7.fhir.r4.model.StringType,org.hl7.fhir.r4.model.StringType)", e.getCause().getMessage()); + } catch (ConfigurationException e) { + assertEquals(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(404) + "@OperationParam detected on method that is not annotated with @Operation: public java.util.List ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$2MyProvider.search(org.hl7.fhir.r4.model.StringType,org.hl7.fhir.r4.model.StringType)", e.getMessage()); } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java index 25194f468b3..ac5491144fc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java @@ -1,16 +1,21 @@ package ca.uhn.fhir.rest.server; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.List; import java.util.Set; @@ -21,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.*; public class ServerMethodSelectionR4Test extends BaseR4ServerTest { - /** * Server method with no _include * Client request with _include @@ -42,7 +46,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); try { - myClient + ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -73,7 +78,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); - Bundle results = myClient + Bundle results = ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -103,7 +109,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); try { - myClient + ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -134,7 +141,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); - Bundle results = myClient + Bundle results = ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java index 6544b49186c..c4a6de6f8e7 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java @@ -10,7 +10,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -21,12 +22,6 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpTrace; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CodeType; @@ -35,16 +30,14 @@ import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -54,19 +47,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerMimetypeR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @BeforeEach - public void before() { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); private String readAndReturnContentType(String theAccept) throws IOException { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); if (theAccept != null) { httpGet.addHeader(Constants.HEADER_ACCEPT, theAccept); } @@ -79,7 +73,7 @@ public class ServerMimetypeR4Test { @Test public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -99,7 +93,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -120,7 +114,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON_NEW + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -142,7 +136,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -164,7 +158,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -186,7 +180,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML_NEW + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -207,7 +201,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -224,7 +218,7 @@ public class ServerMimetypeR4Test { @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -242,7 +236,7 @@ public class ServerMimetypeR4Test { return "TRACK"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -258,7 +252,7 @@ public class ServerMimetypeR4Test { */ @Test public void testResponseContentTypesJson() throws IOException { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); // None given assertEquals("application/fhir+xml", readAndReturnContentType(null)); @@ -281,7 +275,7 @@ public class ServerMimetypeR4Test { */ @Test public void testResponseContentTypesXml() throws IOException { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); // None given assertEquals("application/fhir+json", readAndReturnContentType(null)); @@ -302,7 +296,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -318,7 +312,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -334,7 +328,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -350,7 +344,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -367,7 +361,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -384,7 +378,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -399,7 +393,7 @@ public class ServerMimetypeR4Test { } private List toStrings(List theFormat) { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); for (CodeType next : theFormat) { retVal.add(next.asStringValue()); } @@ -434,7 +428,7 @@ public class ServerMimetypeR4Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); @@ -453,31 +447,7 @@ public class ServerMimetypeR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java index 155a8c89825..8f93d6d1f68 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java @@ -7,7 +7,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -17,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; @@ -29,6 +31,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Collections; @@ -46,13 +49,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SummaryParamR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static SummaryEnum ourLastSummary; private static List ourLastSummaryList; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyMedicationRequestProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -63,7 +72,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode(), + ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode(), Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -80,7 +89,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -97,7 +106,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationRequest/1?_summary=" + SummaryEnum.TEXT.getCode()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -114,7 +123,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryTrue() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode(); + String url = ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode(); verifyXmlAndJson( url, Patient.class, @@ -133,7 +142,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryCount() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -150,7 +159,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryCountAndData() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode() + "," + SummaryEnum.DATA.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode() + "," + SummaryEnum.DATA.getCode(); verifyXmlAndJson( url, bundle -> { @@ -166,7 +175,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryData() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.DATA.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.DATA.getCode(); verifyXmlAndJson( url, bundle -> { @@ -183,7 +192,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryFalse() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=false"; + String url = ourServer.getBaseUrl() + "/Patient?_summary=false"; verifyXmlAndJson( url, bundle -> { @@ -199,7 +208,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryText() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TEXT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TEXT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -216,7 +225,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTextWithMandatory() throws Exception { - String url = "http://localhost:" + ourPort + "/MedicationRequest?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"; + String url = ourServer.getBaseUrl() + "/MedicationRequest?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"; verifyXmlAndJson( url, bundle -> { @@ -234,7 +243,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTextMulti() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -251,7 +260,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTrue() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TRUE.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TRUE.getCode(); verifyXmlAndJson( url, bundle -> { @@ -268,7 +277,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryWithTextAndOthers() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=text&_summary=data"; + String url = ourServer.getBaseUrl() + "/Patient?_summary=text&_summary=data"; try (CloseableHttpResponse status = ourClient.execute(new HttpGet(url))) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -373,29 +382,7 @@ public class SummaryParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationRequestProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java index 784bd9d68c9..237e72a218c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java @@ -47,10 +47,11 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.util.Assert; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java index 06362fa443d..e133f302a34 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java @@ -27,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.StringContains; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.OperationOutcome; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java index 1b7d6b7f344..aa128750d69 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java @@ -2,12 +2,16 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -17,18 +21,19 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.List; import java.util.concurrent.TimeUnit; @@ -41,23 +46,28 @@ import static org.mockito.Mockito.when; public class ExceptionInterceptorMethodTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionInterceptorMethodTest.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; private IServerInterceptor myInterceptor; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { myInterceptor = mock(IServerInterceptor.class); - servlet.getInterceptorService().registerInterceptor(myInterceptor); + ourServer.getInterceptorService().registerInterceptor(myInterceptor); } @AfterEach public void after() { - servlet.getInterceptorService().unregisterInterceptor(myInterceptor); + ourServer.getInterceptorService().unregisterInterceptor(myInterceptor); } @Test @@ -67,7 +77,7 @@ public class ExceptionInterceptorMethodTest { when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=throwUnprocessableEntityException"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { ourLog.info(IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8)); assertEquals(422, status.getStatusLine().getStatusCode()); @@ -94,7 +104,7 @@ public class ExceptionInterceptorMethodTest { return false; }); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=throwUnprocessableEntityException"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -106,33 +116,12 @@ public class ExceptionInterceptorMethodTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); + - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java index 220ec958e59..73ccf4884bc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java @@ -6,31 +6,26 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -39,17 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class InjectionAttackTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InjectionAttackTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .setDefaultResponseEncoding(EncodingEnum.JSON); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testPreventHtmlInjectionViaInvalidContentType() throws Exception { - String requestUrl = "http://localhost:" + - ourPort + - "/Patient/123"; + String requestUrl = ourServer.getBaseUrl() + "/Patient/123"; // XML HTML HttpGet httpGet = new HttpGet(requestUrl); @@ -65,8 +64,7 @@ public class InjectionAttackTest { @Test public void testPreventHtmlInjectionViaInvalidParameterName() throws Exception { - String requestUrl = "http://localhost:" + - ourPort + + String requestUrl = ourServer.getBaseUrl() + "/Patient?a" + UrlUtil.escapeUrlParam("