Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 15 additions & 2 deletions src/ig/IGExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ const IG_RESOURCE_FORMAT_EXTENSIONS = [
DEPRECATED_RESOURCE_FORMAT_EXTENSION,
CURRENT_RESOURCE_FORMAT_EXTENSION
];
// FHIR ImplementationGuide.name invariant: name.matches('[A-Z]([A-Za-z0-9_]){0,254}')
const IG_NAME_REGEX = /^[A-Z]([A-Za-z0-9_]){0,254}$/;

function isR4(fhirVersion: string[]) {
return fhirVersion.some(v => /^R4B?$/.test(getFHIRVersionInfo(v).name));
Expand Down Expand Up @@ -161,8 +163,7 @@ export class IGExporter {
modifierExtension: this.config.modifierExtension,
url: this.config.url ?? `${this.config.canonical}/ImplementationGuide/${this.config.id}`,
version: this.config.version,
// name must be alphanumeric (allowing underscore as well)
name: this.config.name.replace(/[^A-Za-z0-9_]/g, ''),
name: this.config.name,
title: this.config.title,
status: this.config.status,
experimental: this.config.experimental,
Expand Down Expand Up @@ -196,6 +197,18 @@ export class IGExporter {
template: this.config.templates
}
};
if (this.config.name != null && !IG_NAME_REGEX.test(this.config.name)) {
logger.warn(
`The ImplementationGuide name "${this.config.name}" does not match the FHIR constraint ` +
"'[A-Z]([A-Za-z0-9_]){0,254}'. Valid names start with an upper-case ASCII letter ('A'..'Z') " +
"followed by any combination of upper- or lower-case ASCII letters ('A'..'Z', and 'a'..'z'), " +
"numerals ('0'-'9') and '_', with a length limit of 255 characters. " +
`Update the "name" property in ${this.configName}.`,
{
file: this.config.filePath
}
);
}
// Add the path-history, if applicable (only applies to HL7 IGs)
if (
/^https?:\/\/hl7.org\//.test(this.config.canonical) &&
Expand Down
59 changes: 59 additions & 0 deletions test/ig/IGExporter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,63 @@ describe('IGExporter', () => {
expect(fs.existsSync(igIniPath)).toBeFalsy();
});
});

describe('#ig-name', () => {
let tempOut: string;
let defs: Awaited<ReturnType<typeof getTestFHIRDefinitions>>;
let configPath: string;
let configYaml: string;

beforeAll(async () => {
defs = await getTestFHIRDefinitions(true, testDefsPath('r4-definitions'));
configPath = path.join(__dirname, '..', 'import', 'fixtures', 'minimal-config.yaml');
configYaml = fs.readFileSync(configPath, 'utf8');
});

beforeEach(() => {
loggerSpy.reset();
tempOut = temp.mkdirSync('sushi-test');
});

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

function exportIGWithName(name: string) {
const config = importConfiguration(configYaml, configPath);
config.name = name;
const pkg = new Package(config);
const exporter = new IGExporter(pkg, defs, __dirname);
exporter.export(tempOut);
const igPath = path.join(
tempOut,
'fsh-generated',
'resources',
'ImplementationGuide-fhir.us.minimal.json'
);
return fs.readJSONSync(igPath);
}

function hasNameConstraintWarning() {
return loggerSpy.getAllMessages('warn').some(m => m.includes('[A-Z]([A-Za-z0-9_]){0,254}'));
}

it('should keep an authored IG name that contains a space and warn about the FHIR name constraint', () => {
const igContent = exportIGWithName('Minimal IG');
expect(igContent.name).toBe('Minimal IG');
expect(hasNameConstraintWarning()).toBe(true);
});

it('should keep a valid IG name and not warn about the FHIR name constraint', () => {
const igContent = exportIGWithName('MinimalIG');
expect(igContent.name).toBe('MinimalIG');
expect(hasNameConstraintWarning()).toBe(false);
});

it('should keep an authored IG name with a hyphen or leading lowercase letter and warn about the FHIR name constraint', () => {
const igContent = exportIGWithName('example-ig');
expect(igContent.name).toBe('example-ig');
expect(hasNameConstraintWarning()).toBe(true);
});
});
});
Loading