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

Fish With Version #1275

Merged
merged 15 commits into from
May 19, 2023
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Assigning a code with an invalid code system should only warn when as…
…signing to a primitive code/string/uri
cmoesel committed May 18, 2023
commit 3a9f3e993622bba56a79e0d2f6e0f0c14e1b4406
24 changes: 20 additions & 4 deletions src/fhirtypes/ElementDefinition.ts
Original file line number Diff line number Diff line change
@@ -2102,18 +2102,34 @@ export class ElementDefinition {
* @throws {InvalidUriError} when the system being assigned is not a valid uri
*/
private assignFshCode(code: FshCode, exactly = false, fisher?: Fishable): void {
const type = this.type[0].code;

if (code.system) {
const csURI = code.system.split('|')[0];
const vsURI = fishForMetadataBestVersion(fisher, code.system, Type.ValueSet)?.url ?? '';
if (vsURI) {
throw new MismatchedBindingTypeError(code.system, this.path, 'CodeSystem');
if (type === 'code' || type === 'string' || type === 'uri') {
logger.warn(
`The fully qualified code ${code.system}#${code.code} is invalid because the specified system is a ValueSet. ` +
`Since ${this.path} is a ${type}, the system will not be used, but this issue should be corrected by ` +
`updating the system to refer to a proper CodeSystem or by specifying a code only (e.g., #${code.code}).`
);
} else {
throw new MismatchedBindingTypeError(code.system, this.path, 'CodeSystem');
}
} else if (!isUri(csURI)) {
throw new InvalidUriError(code.system);
if (type === 'code' || type === 'string' || type === 'uri') {
logger.warn(
`The fully qualified code ${code.system}#${code.code} is invalid because the specified system is not a URI. ` +
`Since ${this.path} is a ${type}, the system will not be used, but this issue should be corrected by ` +
`updating the system to refer to a proper CodeSystem or by specifying a code only (e.g., #${code.code}).`
);
} else {
throw new InvalidUriError(code.system);
}
}
}

const type = this.type[0].code;

if (type === 'code' || type === 'string' || type === 'uri') {
this.assignFHIRValue(code.toString(), code.code, exactly, type);
} else if (type === 'CodeableConcept') {
63 changes: 59 additions & 4 deletions test/fhirtypes/ElementDefinition.assignFshCode.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
import cloneDeep from 'lodash/cloneDeep';
import { loadFromPath } from 'fhir-package-loader';
import { TestFisher } from '../testhelpers';
import { TestFisher, loggerSpy } from '../testhelpers';
import { FHIRDefinitions } from '../../src/fhirdefs/FHIRDefinitions';
import { StructureDefinition } from '../../src/fhirtypes/StructureDefinition';
import { FshCode } from '../../src/fshtypes/FshCode';
@@ -28,6 +28,7 @@ describe('ElementDefinition', () => {
barFooCode = new FshCode('foo', 'http://bar.com');
versionedCode = new FshCode('versioned', 'http://versioned.com|7.6.5');
codeWithDisplay = new FshCode('bar', 'http://foo.com', 'Foo Bar');
loggerSpy.reset();
});

describe('#assignFshCode()', () => {
@@ -167,7 +168,7 @@ describe('ElementDefinition', () => {
expect(clone).toEqual(concept);
});

it('should throw InvalidUriError when binding with a non-URI value', () => {
it('should throw InvalidUriError when assigning a code with a non-URI value', () => {
const category = observation.elements.find(e => e.id === 'Observation.category');
const clone = cloneDeep(category);
expect(() => {
@@ -178,7 +179,30 @@ describe('ElementDefinition', () => {
}).toThrow(/notAUri/);
});

it('should throw MismatchedBindingTypeError when binding with a ValueSet as a system', () => {
it('should NOT throw InvalidUriError when assigning a code with a non-URI value to a primitive code', () => {
const status = observation.elements.find(e => e.id === 'Observation.status');
const clone = cloneDeep(status);
clone.assignValue(new FshCode('code', 'notAUri'));
expect(loggerSpy.getAllLogs('warn')).toHaveLength(1);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/code notAUri#code is invalid.*specified system is not a URI/
);
expect(loggerSpy.getLastMessage('warn')).toMatch(/Observation.status is a code/);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/specifying a code only \(e\.g\., #code\)\./
);
clone.assignValue(new FshCode('code', 'notAUri'), true);
expect(loggerSpy.getAllLogs('warn')).toHaveLength(2);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/code notAUri#code is invalid.*specified system is not a URI/
);
expect(loggerSpy.getLastMessage('warn')).toMatch(/Observation.status is a code/);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/specifying a code only \(e\.g\., #code\)\./
);
});

it('should throw MismatchedBindingTypeError when assigning a code with a ValueSet as a system', () => {
const category = observation.elements.find(e => e.id === 'Observation.category');
const clone = cloneDeep(category);
expect(() => {
@@ -197,7 +221,7 @@ describe('ElementDefinition', () => {
}).toThrow(/CodeSystem/);
});

it('should throw MismatchedBindingTypeError when binding with a ValueSet as a system when provided with a version', () => {
it('should throw MismatchedBindingTypeError when assigning a code with a ValueSet as a system when provided with a version', () => {
const category = observation.elements.find(e => e.id === 'Observation.category');
const clone = cloneDeep(category);
expect(() => {
@@ -216,6 +240,37 @@ describe('ElementDefinition', () => {
}).toThrow(/CodeSystem/);
});

it('should NOT throw MismatchedBindingTypeError when assigning a code with a ValueSet as a system to a primitive code', () => {
const status = observation.elements.find(e => e.id === 'Observation.status');
const clone = cloneDeep(status);
clone.assignValue(
new FshCode('final', 'http://hl7.org/fhir/ValueSet/observation-status'),
false,
fisher
);
expect(loggerSpy.getAllLogs('warn')).toHaveLength(1);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/code http:\/\/hl7\.org\/fhir\/ValueSet\/observation-status#final is invalid.*specified system is a ValueSet/
);
expect(loggerSpy.getLastMessage('warn')).toMatch(/Observation.status is a code/);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/specifying a code only \(e\.g\., #final\)\./
);
clone.assignValue(
new FshCode('final', 'http://hl7.org/fhir/ValueSet/observation-status'),
true,
fisher
);
expect(loggerSpy.getAllLogs('warn')).toHaveLength(2);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/code http:\/\/hl7\.org\/fhir\/ValueSet\/observation-status#final is invalid.*specified system is a ValueSet/
);
expect(loggerSpy.getLastMessage('warn')).toMatch(/Observation.status is a code/);
expect(loggerSpy.getLastMessage('warn')).toMatch(
/specifying a code only \(e\.g\., #final\)\./
);
});

it('should assign a code to a Coding', () => {
const concept = observation.elements.find(e => e.id === 'Observation.code');
concept.unfold(fisher);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"resourceType":"ValueSet","id":"observation-status","meta":{"lastUpdated":"2019-11-01T09:29:23.356+11:00","profile":["http://hl7.org/fhir/StructureDefinition/shareablevalueset"]},"extension":[{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-wg","valueCode":"oo"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status","valueCode":"normative"},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm","valueInteger":5},{"url":"http://hl7.org/fhir/StructureDefinition/structuredefinition-normative-version","valueCode":"4.0.0"}],"url":"http://hl7.org/fhir/ValueSet/observation-status","identifier":[{"system":"urn:ietf:rfc:3986","value":"urn:oid:2.16.840.1.113883.4.642.3.400"}],"version":"4.0.1","name":"ObservationStatus","title":"ObservationStatus","status":"active","experimental":false,"date":"2019-11-01T09:29:23+11:00","publisher":"HL7 (FHIR Project)","contact":[{"telecom":[{"system":"url","value":"http://hl7.org/fhir"},{"system":"email","value":"[email protected]"}]}],"description":"Codes providing the status of an observation.","immutable":true,"compose":{"include":[{"system":"http://hl7.org/fhir/observation-status"}]}}