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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,45 @@
*/
package ca.uhn.fhir.util;

import java.util.StringTokenizer;
import ca.uhn.fhir.i18n.Msg;
import org.apache.commons.lang3.ArrayUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

import static org.apache.commons.lang3.StringUtils.isBlank;

public class UrlPathTokenizer {

private final StringTokenizer myTok;
private String[] tokens;
private int curPos;

public UrlPathTokenizer(String theRequestPath) {
myTok = new StringTokenizer(theRequestPath, "/");
if (theRequestPath == null) {
theRequestPath = "";
}
tokens = removeBlanksAndSanitize(theRequestPath.split("/"));
curPos = 0;
}

public boolean hasMoreTokens() {
return myTok.hasMoreTokens();
return curPos < tokens.length;
}

public int countTokens() {
return tokens.length;
}

/**
* Returns the next token without updating the current position.
* Will throw NoSuchElementException if there are no more tokens.
*/
public String peek() {
if (!hasMoreTokens()) {
throw new NoSuchElementException(Msg.code(2420) + "Attempt to retrieve URL token out of bounds");
}
return tokens[curPos];
}

/**
Expand All @@ -43,6 +70,22 @@ public boolean hasMoreTokens() {
* @see UrlUtil#unescape(String)
*/
public String nextTokenUnescapedAndSanitized() {
return UrlUtil.sanitizeUrlPart(UrlUtil.unescape(myTok.nextToken()));
String token = peek();
curPos++;
return token;
}

/**
* Given an array of Strings, this method will return all the non-blank entries in that
* array, after running sanitizeUrlPart() and unescape() on them.
*/
private static String[] removeBlanksAndSanitize(String[] theInput) {
List<String> output = new ArrayList<>();
for (String s : theInput) {
if (!isBlank(s)) {
output.add(UrlUtil.sanitizeUrlPart(UrlUtil.unescape(s)));
}
}
return output.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: fix
issue: 5117
title: "Previously, all MDM field scores, including `NO_MATCH`es, were included in the final total MDM score. This has
now been fixed so that only `MATCH`ed fields are included in the total MDM score."
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: fix
issue: 5119
jira: SMILE-7090
title: "Previously, when the consent service would remove all resources to be returned, the response bundle would
not provide the previous/next link(s). This has been corrected."
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: fix
issue: 5126
title: "Previously, updating from Hapi-fhir 6.6.0 to 6.8.0 would cause migration error, it is now fixed."
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,13 @@ protected void init680() {
.withColumns("RES_VER_PID")
.failureAllowed();

// drop the index for any database that has RES_PID column already indexed from previous migrations
version.onTable("HFJ_RES_VER_PROV")
.addIndex("20230510.2", "IDX_RESVERPROV_RES_PID")
.dropIndex("20230510.2", "FK_RESVERPROV_RES_PID")
.failureAllowed();

version.onTable("HFJ_RES_VER_PROV")
.addIndex("20230510.3", "IDX_RESVERPROV_RES_PID")
.unique(false)
.withColumns("RES_PID");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,13 @@ private NpmPackage addPackageToCacheInternal(NpmPackageData thePackageData) {

String dirName = "package";
NpmPackage.NpmPackageFolder packageFolder = npmPackage.getFolders().get(dirName);
for (Map.Entry<String, List<String>> nextTypeToFiles :
packageFolder.getTypes().entrySet()) {
Map<String, List<String>> packageFolderTypes = null;
try {
packageFolderTypes = packageFolder.getTypes();
} catch (IOException e) {
throw new InternalErrorException(Msg.code(2371) + e);
}
for (Map.Entry<String, List<String>> nextTypeToFiles : packageFolderTypes.entrySet()) {
String nextType = nextTypeToFiles.getKey();
for (String nextFile : nextTypeToFiles.getValue()) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ public List<IBaseResource> parseResourcesOfType(String theType, NpmPackage thePk
return Collections.emptyList();
}
ArrayList<IBaseResource> resources = new ArrayList<>();
List<String> filesForType =
thePkg.getFolders().get("package").getTypes().get(theType);
List<String> filesForType = null;
try {
filesForType = thePkg.getFolders().get("package").getTypes().get(theType);
} catch (IOException e) {
throw new InternalErrorException(
Msg.code(2370) + "Cannot install resource of type " + theType + ": Could not get types", e);
}
if (filesForType != null) {
for (String file : filesForType) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;
Expand Down Expand Up @@ -125,6 +126,36 @@ public void testSearch_NoExplicitCount() {

}

@Test
public void testPagingNextLink_whenAllResourcesHaveBeenReturned_willNotBePresent(){

myServer.setDefaultPageSize(5);

for (int i = 0; i < 10; i++) {
createPatient(withId("A" + i), withActiveTrue());
}

Bundle outcome = myClient
.search()
.forResource("Patient")
.where(Patient.ACTIVE.exactly().code("true"))
.returnBundle(Bundle.class)
.execute();

assertThat(outcome.getEntry(), hasSize(5));

Bundle secondPageBundle = myClient.loadPage().next(outcome).execute();

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());

}



@Test
public void testSearch_WithExplicitCount() {
Expand Down Expand Up @@ -179,7 +210,6 @@ public void testSearch_WithExplicitCount() {
assertEquals(1, myCaptureQueriesListener.countCommits());
assertEquals(0, myCaptureQueriesListener.countRollbacks());

assertThat(outcome.getLink(Constants.LINK_NEXT).getUrl(), containsString("Patient?_count=7&_offset=14&active=true"));
assertThat(outcome.getLink(Constants.LINK_PREVIOUS).getUrl(), containsString("Patient?_count=7&_offset=0&active=true"));

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,42 @@ public void testGraphQL_MaskLinkedResource() throws IOException {

}

@Test
public void testPaging_whenResourceViewingIsRejected_responseBundleWillHaveNextLink(){
// given
create50Observations();

myConsentInterceptor = new ConsentInterceptor(new ConsentSvcRejectWillSeeResource());
myServer.getRestfulServer().getInterceptorService().registerInterceptor(myConsentInterceptor);

// when
Bundle results = myClient.search().forResource(Observation.class).count(10).returnBundle(Bundle.class).execute();
assertThat(results.getEntry(), hasSize(0));

// then
String nextUrl = BundleUtil.getLinkUrlOfType(myFhirContext, results, "next");
assertThat(nextUrl, containsString("_getpagesoffset=10"));

}

@Test
public void testPaging_whenResourceViewingIsRejected_secondPageWillHavePreviousLink(){
// given
create50Observations();

myConsentInterceptor = new ConsentInterceptor(new ConsentSvcRejectWillSeeResource());
myServer.getRestfulServer().getInterceptorService().registerInterceptor(myConsentInterceptor);

// when
Bundle results = myClient.search().forResource(Observation.class).count(10).returnBundle(Bundle.class).execute();
Bundle nextResults = myClient.loadPage().next(results).execute();

// then
String previous = BundleUtil.getLinkUrlOfType(myFhirContext, nextResults, "previous");
assertThat(previous, containsString("_getpagesoffset=0"));

}

private void createPatientAndOrg() {
myPatientIds = new ArrayList<>();

Expand Down Expand Up @@ -1062,5 +1098,15 @@ public void completeOperationFailure(RequestDetails theRequestDetails, BaseServe

}

private static class ConsentSvcRejectWillSeeResource implements IConsentService {
@Override
public ConsentOutcome willSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) {
if("Bundle".equals(theResource.fhirType())){
return new ConsentOutcome(ConsentOperationStatusEnum.PROCEED);
}
return new ConsentOutcome(ConsentOperationStatusEnum.REJECT);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2482,12 +2482,8 @@ public void testHistoryPaging() {
"Patient/A/_history/1"
));

history = myClient
.loadPage()
.next(history)
.execute();

assertEquals(0, history.getEntry().size());
// we got them all
assertNull(history.getLink("next"));

/*
* Try with a date offset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ void validateJsonReturnsNullWhenInputIsEmptyString() {
@Test
void validateJsonThrowsExceptionWhenInputIsInvalid() {
// setup
final String expected = "Invalid JSON: Unrecognized token 'abc': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n" +
final String expected = "HAPI-2378: Invalid JSON: Unrecognized token 'abc': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\n" +
" at [Source: (String)\"abc\"; line: 1, column: 4]";
// execute
final UnprocessableEntityException actual = assertThrows(UnprocessableEntityException.class, () -> myFixture.validateJson("abc"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ void testParseResourceWithNoResourceType() {
IBaseResource srq = myCdsPrefetchFhirClientSvc.resourceFromUrl(cdsServiceRequestJson, "1234");
fail("should throw, no resource present");
} catch (InvalidRequestException e) {
assertEquals("Unable to translate url 1234 into a resource or a bundle.", e.getMessage());
assertEquals("HAPI-2384: Unable to translate url 1234 into a resource or a bundle.", e.getMessage());
}
}

Expand All @@ -106,7 +106,7 @@ void testParseResourceWithNoResourceTypeAndSlash() {
IBaseResource srq = myCdsPrefetchFhirClientSvc.resourceFromUrl(cdsServiceRequestJson, "/1234");
fail("should throw, no resource present");
} catch (InvalidRequestException e) {
assertEquals("Failed to resolve /1234. Url does not start with a resource type.", e.getMessage());
assertEquals("HAPI-2383: Failed to resolve /1234. Url does not start with a resource type.", e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void testShouldThrowForMissingPrefetchTokens() {
PrefetchTemplateUtil.substituteTemplate(template, context, FhirContext.forR4());
fail();
} catch (InvalidRequestException e) {
assertEquals("Either request context was empty or it did not provide a value for key <userId>. Please make sure you are including a context with valid keys.", e.getMessage());
assertEquals("HAPI-2375: Either request context was empty or it did not provide a value for key <userId>. Please make sure you are including a context with valid keys.", e.getMessage());
}
}

Expand All @@ -50,7 +50,7 @@ public void testShouldThrow400ForMissingContext() {
PrefetchTemplateUtil.substituteTemplate(template, context, FhirContext.forR4());
fail();
} catch (InvalidRequestException e) {
assertEquals("Either request context was empty or it did not provide a value for key <userId>. Please make sure you are including a context with valid keys.", e.getMessage());
assertEquals("HAPI-2375: Either request context was empty or it did not provide a value for key <userId>. Please make sure you are including a context with valid keys.", e.getMessage());
}
}

Expand All @@ -63,7 +63,7 @@ public void testShouldThrowForMissingNestedPrefetchTokens() {
PrefetchTemplateUtil.substituteTemplate(template, context, FhirContext.forR4());
fail();
} catch (InvalidRequestException e) {
assertEquals("Request context did not provide a value for key <draftOrders>. Available keys in context are: [patientId]", e.getMessage());
assertEquals("HAPI-2372: Request context did not provide a value for key <draftOrders>. Available keys in context are: [patientId]", e.getMessage());
}
}

Expand Down Expand Up @@ -119,7 +119,7 @@ public void testShouldThrowForDaVinciTemplateIfResourcesAreNotFoundInContextForR
PrefetchTemplateUtil.substituteTemplate(template, context, FhirContext.forR4());
fail("substituteTemplate call was successful with a null context field.");
} catch (InvalidRequestException e) {
assertEquals("Request context did not provide for resource(s) matching template. ResourceType missing is: ServiceRequest", e.getMessage());
assertEquals("HAPI-2373: Request context did not provide for resource(s) matching template. ResourceType missing is: ServiceRequest", e.getMessage());
}
}

Expand All @@ -134,7 +134,7 @@ public void testShouldThrowForDaVinciTemplateIfResourceIsNotBundle() {
PrefetchTemplateUtil.substituteTemplate(template, context, FhirContext.forR4());
fail();
} catch (InvalidRequestException e) {
assertEquals("Request context did not provide valid " + fhirContextR4.getVersion().getVersion() + " Bundle resource for template key <draftOrders>" , e.getMessage());
assertEquals("HAPI-2374: Request context did not provide valid " + fhirContextR4.getVersion().getVersion() + " Bundle resource for template key <draftOrders>", e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,13 @@ private MdmMatchOutcome getMatchOutcome(IBaseResource theLeftResource, IBaseReso
fieldComparator.getName(),
matchEvaluation.score,
vector);
score += matchEvaluation.score;
} else {
ourLog.trace(
"No match: Matcher {} did not match (score: {}).",
fieldComparator.getName(),
matchEvaluation.score);
}
score += matchEvaluation.score;
appliedRuleCount += 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,19 @@ public void testMatchResult() {
patient3.addName().addGiven("Henry");
assertMatchResult(MdmMatchResultEnum.NO_MATCH, 0L, 0.0, false, false, myMdmResourceMatcherSvc.getMatchResult(myJohn, patient3));
}

@Test
public void testScoreOnlySummedWhenMatchFieldMatches() {
MdmMatchOutcome outcome = myMdmResourceMatcherSvc.getMatchResult(myJohn, myJohny);
assertMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH, 1L, 0.816, false, false, outcome);

myJohn.addName().setFamily("Smith");
myJohny.addName().setFamily("htims");
outcome = myMdmResourceMatcherSvc.getMatchResult(myJohn, myJohny);
assertMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH, 1L, 0.816, false, false, outcome);

myJohny.addName().setFamily("Smith");
outcome = myMdmResourceMatcherSvc.getMatchResult(myJohn, myJohny);
assertMatchResult(MdmMatchResultEnum.MATCH, 3L, 1.816, false, false, outcome);
}
}
Loading