From bd562abe64be569f2a5df4d272545b7dda4d0634 Mon Sep 17 00:00:00 2001 From: Taha Date: Mon, 13 Nov 2023 19:56:13 -0800 Subject: [PATCH] Update package operation output to distinguish collection and transaction (#749) * [APHL-778] update package output to distinguish collection and transaction * [APHL-778] Updated tests * [APHL-778] no total for transactions --------- Co-authored-by: taha.attari@smilecdr.com --- .../ruler/cr/KnowledgeArtifactProcessor.java | 51 +++++++++++--- .../ruler/cr/r4/RepositoryServiceTest.java | 69 +++++++++++++++++++ 2 files changed, 109 insertions(+), 11 deletions(-) diff --git a/plugin/cr/src/main/java/org/opencds/cqf/ruler/cr/KnowledgeArtifactProcessor.java b/plugin/cr/src/main/java/org/opencds/cqf/ruler/cr/KnowledgeArtifactProcessor.java index d54d500e8..69be5d6af 100644 --- a/plugin/cr/src/main/java/org/opencds/cqf/ruler/cr/KnowledgeArtifactProcessor.java +++ b/plugin/cr/src/main/java/org/opencds/cqf/ruler/cr/KnowledgeArtifactProcessor.java @@ -23,6 +23,7 @@ import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleEntryRequestComponent; +import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.CodeType; @@ -764,9 +765,12 @@ public Bundle createPackageBundle(IdType id, FhirDal fhirDal, List capab if (packageOnly != null) { throw new NotImplementedOperationException("This repository is not implementing packageOnly at this time"); } + if (count != null && count < 0) { + throw new UnprocessableEntityException("'count' must be non-negative"); + } MetadataResource resource = (MetadataResource) fhirDal.read(id); // TODO: In the case of a released (active) root Library we can depend on the relatedArtifacts as a comprehensive manifest - Bundle packagedBundle = new Bundle().setType(Bundle.BundleType.COLLECTION); + Bundle packagedBundle = new Bundle(); if (include != null && include.size() == 1 && include.stream().anyMatch((includedType) -> includedType.equals("artifact"))) { @@ -782,33 +786,58 @@ public Bundle createPackageBundle(IdType id, FhirDal fhirDal, List capab List included = findUnsupportedInclude(packagedBundle.getEntry(),include); packagedBundle.setEntry(included); } - packagedBundle.setTotal(packagedBundle.getEntry().size()); + setCorrectBundleType(count,offset,packagedBundle); + pageBundleBasedOnCountAndOffset(count, offset, packagedBundle); + handlePriority(resource, packagedBundle.getEntry()); + return packagedBundle; + } + private void pageBundleBasedOnCountAndOffset(Integer count, Integer offset, Bundle bundle) { if (offset != null) { - List entries = packagedBundle.getEntry(); + List entries = bundle.getEntry(); Integer bundleSize = entries.size(); if (offset < bundleSize) { - packagedBundle.setEntry(entries.subList(offset, bundleSize)); + bundle.setEntry(entries.subList(offset, bundleSize)); } else { - packagedBundle.setEntry(Arrays.asList()); + bundle.setEntry(Arrays.asList()); } } if (count != null) { // repeat these two from earlier because we might modify / replace // the entries list at any time - List entries = packagedBundle.getEntry(); + List entries = bundle.getEntry(); Integer bundleSize = entries.size(); if (count < bundleSize){ - packagedBundle.setEntry(entries.subList(0, count)); + bundle.setEntry(entries.subList(0, count)); } else { // there are not enough entries in the bundle to page, so // we return all of them no change } } - handlePriority(resource, packagedBundle.getEntry()); - return packagedBundle; } - - void handlePriority(MetadataResource resource, List bundleEntries) { + private void setCorrectBundleType(Integer count, Integer offset, Bundle bundle) { + // if the bundle is paged then it must be of type = collection + // and modified to follow bundle.type constraints + // if not, set type = transaction + // special case of count = 0 -> set type = searchset so we can display bundle.total + if (count != null && count == 0) { + bundle.setType(BundleType.SEARCHSET); + bundle.setTotal(bundle.getEntry().size()); + } else if ( + (offset != null && offset > 0) || + (count != null && count < bundle.getEntry().size()) + ) { + bundle.setType(BundleType.COLLECTION); + List removedRequest = bundle.getEntry().stream() + .map(entry -> { + entry.setRequest(null); + return entry; + }).collect(Collectors.toList()); + bundle.setEntry(removedRequest); + } else { + bundle.setType(BundleType.TRANSACTION); + } + } + private void handlePriority(MetadataResource resource, List bundleEntries) { KnowledgeArtifactAdapter adapter = new KnowledgeArtifactAdapter(resource); List valueSets = bundleEntries.stream() .filter(entry -> entry.getResource().getResourceType().equals(ResourceType.ValueSet)) diff --git a/plugin/cr/src/test/java/org/opencds/cqf/ruler/cr/r4/RepositoryServiceTest.java b/plugin/cr/src/test/java/org/opencds/cqf/ruler/cr/r4/RepositoryServiceTest.java index 0df872fd3..f5ac4e871 100644 --- a/plugin/cr/src/test/java/org/opencds/cqf/ruler/cr/r4/RepositoryServiceTest.java +++ b/plugin/cr/src/test/java/org/opencds/cqf/ruler/cr/r4/RepositoryServiceTest.java @@ -26,6 +26,7 @@ import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeableConcept; @@ -1013,6 +1014,8 @@ void packageOperation_should_respect_count_offset() { .returnResourceType(Bundle.class) .execute(); assertTrue(offset4Bundle.getEntry().size() == (countZeroBundle.getTotal() - 4)); + assertTrue(offset4Bundle.getType() == BundleType.COLLECTION); + assertTrue(offset4Bundle.hasTotal() == false); Parameters offsetMaxParams = parameters( part("offset", new IntegerType(countZeroBundle.getTotal())) ); @@ -1036,6 +1039,72 @@ void packageOperation_should_respect_count_offset() { assertTrue(offsetMaxRandomCountBundle.getEntry().size() == 0); } @Test + void packageOperation_different_bundle_types() { + loadTransaction("ersd-small-active-bundle.json"); + Parameters countZeroParams = parameters( + part("count", new IntegerType(0)) + ); + Bundle countZeroBundle = getClient().operation() + .onInstance(specificationLibReference) + .named("$crmi.package") + .withParameters(countZeroParams) + .returnResourceType(Bundle.class) + .execute(); + assertTrue(countZeroBundle.getType() == BundleType.SEARCHSET); + Parameters countSevenParams = parameters( + part("count", new IntegerType(7)) + ); + Bundle countSevenBundle = getClient().operation() + .onInstance(specificationLibReference) + .named("$crmi.package") + .withParameters(countSevenParams) + .returnResourceType(Bundle.class) + .execute(); + assertTrue(countSevenBundle.getType() == BundleType.TRANSACTION); + Parameters countFourParams = parameters( + part("count", new IntegerType(4)) + ); + Bundle countFourBundle = getClient().operation() + .onInstance(specificationLibReference) + .named("$crmi.package") + .withParameters(countFourParams) + .returnResourceType(Bundle.class) + .execute(); + assertTrue(countFourBundle.getType() == BundleType.COLLECTION); + // these assertions test for Bundle base profile conformance when type = collection + assertFalse(countFourBundle.getEntry().stream().anyMatch(entry -> entry.hasRequest())); + assertFalse(countFourBundle.hasTotal()); + Parameters offsetOneParams = parameters( + part("offset", new IntegerType(1)) + ); + Bundle offsetOneBundle = getClient().operation() + .onInstance(specificationLibReference) + .named("$crmi.package") + .withParameters(offsetOneParams) + .returnResourceType(Bundle.class) + .execute(); + assertTrue(offsetOneBundle.getType() == BundleType.COLLECTION); + // these assertions test for Bundle base profile conformance when type = collection + assertFalse(offsetOneBundle.getEntry().stream().anyMatch(entry -> entry.hasRequest())); + assertFalse(offsetOneBundle.hasTotal()); + + Parameters countOneOffsetOneParams = parameters( + part("count", new IntegerType(1)), + part("offset", new IntegerType(1)) + ); + Bundle countOneOffsetOneBundle = getClient().operation() + .onInstance(specificationLibReference) + .named("$crmi.package") + .withParameters(countOneOffsetOneParams) + .returnResourceType(Bundle.class) + .execute(); + assertTrue(countOneOffsetOneBundle.getType() == BundleType.COLLECTION); + // these assertions test for Bundle base profile conformance when type = collection + assertFalse(countOneOffsetOneBundle.getEntry().stream().anyMatch(entry -> entry.hasRequest())); + assertFalse(countOneOffsetOneBundle.hasTotal()); + + } + @Test void packageOperation_should_conditionally_create() { loadTransaction("ersd-small-active-bundle.json"); Parameters emptyParams = parameters();