Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

URL Encode Cross-Version Extension URLs #1549

Merged
merged 6 commits into from
Dec 30, 2024
Merged

Conversation

cmoesel
Copy link
Member

@cmoesel cmoesel commented Dec 23, 2024

Description: Cross-version extensions can represent choice elements, which means that [x] might be part of the extension URL -- in which case, it should be URL-encoded (per specifications) to %5Bx%5D. This also means that the corresponding id should also be URL-encoded to match the tail-end of the URL. Similarly, relative URLs in sub-extensions of complex extensions should also be URL-encoded and the slice names should match the URL-encoded relative URLs. See this Zulip discussion for reference: https://chat.fhir.org/#narrow/channel/215610-shorthand/topic/Issue.20with.20polymorphic.20inter-version.20extensions/near/488766521

This PR does all those things. It follows the approach of being liberal in what it accepts and conservative in what it produces -- which is to say that authors can use unencoded [x] in the URLs and slice names and SUSHI will correctly handle them, but SUSHI will always produce outputs with encoded %5Bx%5D. This PR also fixes the parseFSHPath function because the old implementation did not deal well with slice names containing [x] -- and, as it turns out, the old approach allowed for syntactically incorrect paths like component[1}.code or component[systolic}.code.

Testing Instructions: See the tests for different variations of examples. One thing you can try:

Profile: MyQuestionnaire
Parent: Questionnaire
* extension contains http://hl7.org/fhir/5.0/StructureDefinition/extension-Questionnaire.versionAlgorithm%5Bx%5D named r5VersionAlgorithm 0..1
* extension[r5VersionAlgorithm].value[x] only string

Instance: MyQuestionnaireInstance
InstanceOf: MyQuestionnaire
* status = #active
* extension[r5VersionAlgorithm].valueString = "My Algorithm"

In the above case, the master branch will not recognize this cross-version extension, but this PR branch should.

You can also use the same example above, but replace the contains rule with this one:

* extension contains http://hl7.org/fhir/5.0/StructureDefinition/extension-Questionnaire.versionAlgorithm[x] named r5VersionAlgorithm 0..1

In this case, the master branch will find the extension, but its output will not encode the URL. On this PR branch, it will find the extension but also output the URL in its encoded form.

Related Issue: #1544

For paths that include [x], the brackets should be URL-encoded in the url elements and, as a result, also encoded in the corresponding element ids and slice names. See: https://chat.fhir.org/#narrow/channel/215610-shorthand/topic/Issue.20with.20polymorphic.20inter-version.20extensions/near/488766521
@cmoesel
Copy link
Member Author

cmoesel commented Dec 23, 2024

NOTE: Running regression does detect changes in some repos. For most, it is just the difference of [x] vs %5Bx%5D in the output. In one case, however, it also detects an invalid path (that previously went by undetected) and issues an error (as well as different output since it cannot process the path correctly).

Affected repos (if you want to see for yourself):

HL7/fhir-us-mcc#master
HL7/sdc#master
HL7/davinci-crd#master
hl7-eu/Oncology#master
hl7-eu/laboratory#master
hl7ch/ch-lab-report#master
HL7/uv-lab-rep-ig#main

Copy link
Collaborator

@mint-thompson mint-thompson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! I only have one minor change to request. It feels a little weird that we're breaking some of the FHIR spec around id, but since this is a temporary measure to maintain tool compatibility until the trailing [x]s get removed from extensions, I think it's fine.

Comment on lines +390 to +392
expect(pathParts[2]).toEqual({
base: 'value[x]'
});
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had forgotten that parseFSHPath doesn't include anything about previous slices for path parts that don't have any slices of their own. It's what we want, but I got scared for a minute.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, me too. I originally rewrote this to include the previous slices even in this case -- and it broken a bunch of tests, so I reverted back to the original approach.

Comment on lines 23 to 27
// Get the content from the outermost bracket pairs. (?![^\[]*\]) ensures we don't
// match nested closing brackets (thank you, claude.ai)
parsedPart.brackets = Array.from(
pathPart.slice(parsedPart.base.length).matchAll(/\[([^\[\]]|\[(?:[^\[\]]*)\])*\]/g)
).map(match => match[0].slice(1, -1));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like claude had the right regular expression in the code, but not the comment. Typical claude!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typical claude!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b6caaee.

Copy link
Collaborator

@mint-thompson mint-thompson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hooray for cross-version extensions!

Copy link
Collaborator

@jafeltra jafeltra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! The repos with regressions all make sense to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants