Skip to content

Commit

Permalink
Support User Provided Examples of Logical Models (#941)
Browse files Browse the repository at this point in the history
* Ignore error that is thrown when converting an uknown FHIR type from XML to JSON

* Do not add an entry to IG resource list for examples of Logical Models that already have a corresponding Binary entry in the config

* Add tests for loading non FHIR resourceType JSON and XML and properly adding to IG resource

* Use .some instead of .find

Co-authored-by: Mint Thompson <[email protected]>

Co-authored-by: Mint Thompson <[email protected]>
  • Loading branch information
jafeltra and mint-thompson authored Oct 22, 2021
1 parent a318d12 commit 740edc0
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/fhirdefs/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ export function loadCustomResources(resourceDir: string, defs: FHIRDefinitions):
continue;
}
} catch (e) {
if (e.message.startsWith('Unknown resource type:')) {
// Skip unknown FHIR resource types. When we have instances of Logical Models,
// the resourceType will not be recognized as a known FHIR resourceType, but that's okay.
continue;
}
logger.error(`Loading ${file} failed with the following error:\n${e.message}`);
continue;
}
Expand Down
9 changes: 8 additions & 1 deletion src/fhirtypes/ImplementationGuide.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ContactDetail, UsageContext } from './metaDataTypes';
import { CodeableConcept, Reference, BackboneElement, DomainResource } from './dataTypes';
import {
BackboneElement,
CodeableConcept,
DomainResource,
Extension,
Reference
} from './dataTypes';

export type ImplementationGuide = DomainResource & {
resourceType: 'ImplementationGuide';
Expand Down Expand Up @@ -60,6 +66,7 @@ export type ImplementationGuideDefinitionResource = {
exampleBoolean?: boolean;
exampleCanonical?: string;
groupingId?: string;
extension?: Extension[];
};

export type ImplementationGuideDefinitionPage = {
Expand Down
19 changes: 18 additions & 1 deletion src/ig/IGExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,24 @@ export class IGExporter {
resource => resource.reference?.reference == referenceKey
);

if (configResource?.omit !== true) {
// For predefined examples of Logical Models, the user must provide an entry in config
// that specifies the reference as Binary/[id], the extension that specifies the resource format,
// and the exampleCanonical that references the LogicalModel the resource is an example of.
// In that case, we do not want to add our own entry for the predefined resource - we just
// want to use the resource entry from the sushi-config.yaml
const hasBinaryExampleReference = (this.config.resources ?? []).some(
resource =>
resource.reference?.reference === `Binary/${resourceJSON.id}` &&
resource.exampleCanonical ===
`${this.config.canonical}/StructureDefinition/${resourceJSON.resourceType}` &&
resource.extension?.some(
e =>
e.url ===
'http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format'
)
);

if (configResource?.omit !== true && !hasBinaryExampleReference) {
const existingIndex = this.ig.definition.resource.findIndex(
r => r.reference.reference === referenceKey
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"resourceType": "CustomLogicalModel",
"id": "example-logical-model"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<CustomLogicalModel>
<id value="custom-logical-model" />
</CustomLogicalModel>
11 changes: 11 additions & 0 deletions test/fhirdefs/load.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ describe('#loadCustomResources', () => {
expect(
defs.getPredefinedResource(path.join(pathToInput, 'examples', 'Patient-MyPatient.json')).id
).toBe('MyPatient');
expect(
defs.getPredefinedResource(
path.join(pathToInput, 'examples', 'Binary-LogicalModelExample.json')
).id
).toBe('example-logical-model');
expect(
defs.getPredefinedResource(
path.join(pathToInput, 'extensions', 'StructureDefinition-patient-birthPlace.json')
Expand Down Expand Up @@ -507,6 +512,12 @@ describe('#loadCustomResources', () => {
});
});

it('should not log an error for invalid FHIR types parsed from XML', () => {
loggerSpy.getAllMessages('error').forEach(m => {
expect(m).not.toMatch(/Unknown resource type:/);
});
});

it('should log an info message when it finds spreadsheets', () => {
expect(loggerSpy.getFirstMessage('info')).toMatch(/Found spreadsheets in directory/);
});
Expand Down
106 changes: 106 additions & 0 deletions test/ig/IGExporter.IG.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1586,6 +1586,112 @@ describe('IGExporter', () => {
});
});

describe('#customized-ig-with-logical-model-example', () => {
let pkg: Package;
let exporter: IGExporter;
let tempOut: string;
let fixtures: string;
let config: Configuration;
let defs: FHIRDefinitions;

beforeAll(() => {
defs = new FHIRDefinitions();
loadFromPath(
path.join(__dirname, '..', 'testhelpers', 'testdefs', 'package'),
'testPackage',
defs
);
fixtures = path.join(__dirname, 'fixtures', 'customized-ig-with-logical-model-example');
loadCustomResources(path.join(fixtures, 'input'), defs);
});

beforeEach(() => {
loggerSpy.reset();
tempOut = temp.mkdirSync('sushi-test');
config = cloneDeep(minimalConfig);
config.resources = [
{
reference: {
reference: 'Binary/example-logical-model-json'
},
extension: [
{
url: 'http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format',
valueCode: 'application/json'
}
],
name: 'Example of LM JSON',
exampleCanonical: `${config.canonical}/StructureDefinition/MyLM`
},
{
reference: {
reference: 'Binary/example-logical-model-xml'
},
extension: [
{
url: 'http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format',
valueCode: 'application/xml'
}
],
name: 'Example of LM XML',
exampleCanonical: `${config.canonical}/StructureDefinition/MyLM`
}
];
pkg = new Package(config);
exporter = new IGExporter(pkg, defs, fixtures);
});

afterAll(() => {
temp.cleanupSync();
});

it('should add logical model and example resource references to the ImplementationGuide resource', () => {
exporter.export(tempOut);
const igPath = path.join(
tempOut,
'fsh-generated',
'resources',
'ImplementationGuide-fhir.us.minimal.json'
);
expect(fs.existsSync(igPath)).toBeTruthy();
const igContent: ImplementationGuide = fs.readJSONSync(igPath);
expect(igContent.definition.resource).toHaveLength(3);
expect(igContent.definition.resource).toContainEqual({
reference: {
reference: 'StructureDefinition/MyLM'
},
name: 'MyLM',
exampleBoolean: false
});
expect(igContent.definition.resource).toContainEqual({
reference: {
reference: 'Binary/example-logical-model-json'
},
extension: [
{
url: 'http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format',
valueCode: 'application/json'
}
],
name: 'Example of LM JSON',
exampleCanonical: `${config.canonical}/StructureDefinition/MyLM`
});
expect(igContent.definition.resource).toContainEqual({
reference: {
reference: 'Binary/example-logical-model-xml'
},
extension: [
{
url: 'http://hl7.org/fhir/StructureDefinition/implementationguide-resource-format',
valueCode: 'application/xml'
}
],
name: 'Example of LM XML',
exampleCanonical: `${config.canonical}/StructureDefinition/MyLM`
});
});
});

describe('#pages-folder-ig', () => {
let pkg: Package;
let exporter: IGExporter;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"resourceType": "MyLM",
"id": "example-logical-model-json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<MyLM>
<id value="example-logical-model-xml" />
</MyLM>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"resourceType": "StructureDefinition",
"id": "MyLM",
"url": "http://hl7.org/fhir/us/mcode/StructureDefinition/MyLM",
"name": "MyLM",
"status": "active",
"fhirVersion": "4.0.1",
"type": "MyLM",
"baseDefinition" : "http://hl7.org/fhir/StructureDefinition/Element",
"kind": "logical",
"derivation": "specialization",
"snapshot": {
"element": [
{
"id": "modeling-woo",
"path": "modeling-woo",
"type": [
{
"code": "boolean"
}
]
}
]
}
}

0 comments on commit 740edc0

Please sign in to comment.