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

Support Context keyword when defining Extensions #1282

Merged
merged 12 commits into from
Jun 9, 2023

Conversation

mint-thompson
Copy link
Collaborator

@mint-thompson mint-thompson commented May 31, 2023

Fixes #1259 and completes task CIMPL-1090.

This adds the new Context keyword that can be used when defining an Extension. The value of the context can be specified in a few different ways:

  • A quoted string
  • A FSH path that starts with the name or id of some StructureDefinition
  • A URL for a StructureDefinition, optionally followed by # and a FSH path

Multiple contexts can be specified in a comma-separated list.

The resulting type of the context depends on what is specified:

  • For a quoted string, the fhirpath type is always used.
  • For an unquoted value that specifies an Extension definition or an element that is part of a complex extension, the extension type is used.
  • For any other element, the element type is used.

This allows for some flexibility for how authors specify the context, while consistently resolving contexts to the more specific extension type whenever possible. The included test cases demonstrate this flexibility.

For more information about extension contexts, the official documentation provides some useful details.

The Context keyword is used to specify the contexts in which the
Extension should be used. Add the keyword to the grammar definition.
Store contexts with source info when importing FSH.
Contexts specified with the Context keyword are applied to Extensions
during export. If the value is quoted, it is a "fhirpath" context. If
the value is not quoted and resolves to an Extension definition, it is
an "extension" context. Otherwise, it is an "element" context.
The Context keyword is used at most once per extension. This is similar
to other metadata keywords. To provide multiple contexts, a
comma-separated list can be provided.
When using the Context keyword on an Extension, validate that the
extension or element specified is something that actually exists. The
element is specified by a FSH path, but is exported as a FHIR element
id. Always use the resource id when the context is an element on a core
FHIR resource. Otherwise, use the URL followed by an element id.
A complex extension may contain more than one level of extension
elements. Traverse the extension by url to find deeply nested
extensions.

When an element path is given as context, it may represent a deeply
nested extension. If so, set the context as extension context. The
element counts as a deeply nested extension if every element along its
path is also an extension.
Contained extensions must be specified the same as any other element by
using a FSH path.
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 really good! I had a few comments inline, as well as this:

One other thing related to this feature: while reviewing this, I realized that we need to add Context to the keywords that get highlighted in the VS Code extension. Ideally, I think we could release a new version of the extension around the same time, or even before, we release this feature in SUSHI.

src/fshtypes/Extension.ts Outdated Show resolved Hide resolved
test/fshtypes/Extension.test.ts Outdated Show resolved Hide resolved
'Could not find resource MysteryExtension to use as extension context.'
);
expect(loggerSpy.getLastMessage('error')).toMatch(/File: Context\.fsh.*Line: 8\D*/s);
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

The last test in the custom resource block reminded me of possibly another test case that might be useful in this section. Is it worthwhile also testing that an invalid FSH path on a resource? I think the error will be triggered by the same logic, so it might be a duplicate test, so curious what you think.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think adding that type of test would be redundant? I'm still just curious to hear what you think.

Copy link
Member

@cmoesel cmoesel left a comment

Choose a reason for hiding this comment

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

This is a really nice implementation of a tricky feature. Very well done. I think you did a great job leveraging some of the functions we already had, resulting in a very clean implementation.

I've left a few comments for your consideration. Thank you!

src/export/StructureDefinitionExporter.ts Outdated Show resolved Hide resolved
src/export/StructureDefinitionExporter.ts Outdated Show resolved Hide resolved
src/export/StructureDefinitionExporter.ts Show resolved Hide resolved
src/export/StructureDefinitionExporter.ts Show resolved Hide resolved
Only set the default context if the Context keyword is unused and there
is no existing context.

Assume that the last # present in an unquoted context is what separates
the URL from the path. This helps with URLs that contain a # character.
Copy link
Member

@cmoesel cmoesel left a comment

Choose a reason for hiding this comment

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

Everything is looking really good! I just found one small bit of dead code that can probably be deleted.

Comment on lines 25 to 32
const contextLines: string[] = [];
this.contexts.forEach(extContext => {
if (extContext.isQuoted) {
contextLines.push(`Context: "${fshifyString(extContext.value)}"`);
} else {
contextLines.push(`Context: ${extContext.value}`);
}
});
Copy link
Member

Choose a reason for hiding this comment

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

As far as I can tell, this is dead code now and can be deleted.

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.

I only wanted to check in one outstanding question, other than what Chris mentioned.

'Could not find resource MysteryExtension to use as extension context.'
);
expect(loggerSpy.getLastMessage('error')).toMatch(/File: Context\.fsh.*Line: 8\D*/s);
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think adding that type of test would be redundant? I'm still just curious to hear what you think.

Copy link
Member

@cmoesel cmoesel left a comment

Choose a reason for hiding this comment

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

I just did some manual testing on this, and unfortunately, it looks like it's not quite ready yet.

Given an extension with multiple contexts like this:

Extension: Foo
Context: Patient, Practitioner, "some.fhirpath"
* value[x] only boolean

We get the following parse errors:

error token recognition error at: ' '
  File: /Users/cmoesel/data/fsh/ScratchPad/input/fsh/scratch.fsh
  Line: 12
error token recognition error at: ' '
  File: /Users/cmoesel/data/fsh/ScratchPad/input/fsh/scratch.fsh
  Line: 12

It also emits errors if we try to separate them on separate lines (one error for every space character).

So... we'll hold this feature back for SUSHI 3.1.0.

Id: some-extension
Context: "some.fhirpath()", Observation.component, http://example.org/MyPatient#identifier,
"another.fhirpath(var, 0)"
`;
Copy link
Member

Choose a reason for hiding this comment

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

We need to add an assertion that confirms that no errors are emitted. As of now, this test does produce errors because the parser doesn't know what to do w/ the spaces between the contexts.

Copy link
Member

@cmoesel cmoesel left a comment

Choose a reason for hiding this comment

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

YES!

image

@cmoesel cmoesel merged commit efcb2fa into master Jun 9, 2023
@cmoesel cmoesel deleted the cimpl-1090-extension-context branch June 9, 2023 17:56
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.

Add ContextElement:, ContextFHIRPath:, and ContextExtension: keywords to Extension
3 participants