Update: [AEA-6376] - cardinality within structured definitions#4570
Update: [AEA-6376] - cardinality within structured definitions#4570bencegadanyi1-nhs wants to merge 37 commits intomasterfrom
Conversation
…ork and refactors generation pipeline step
4c20d15 to
883c38d
Compare
There was a problem hiding this comment.
Pull request overview
Updates the fhir-schema-generation package to generate JSON Schemas from Simplifier FHIR StructureDefinitions (including cardinality handling), migrates its test suite from Jest to Vitest, and refreshes frontend Jest snapshots impacted by styling/classname changes.
Changes:
- Added a schema generation pipeline (download → parse → generate → write schemas) for FHIR StructureDefinitions.
- Migrated
packages/fhir-schema-generationtests/config from Jest to Vitest and added extensive unit tests. - Updated multiple client snapshot files (styled-components class hash changes) and added a local Grype scanning Makefile target.
Reviewed changes
Copilot reviewed 31 out of 32 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/tool/site/client/tests/pages/snapshots/validatePage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/pages/snapshots/signPage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/pages/snapshots/returnPage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/pages/snapshots/loginPage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/pages/snapshots/doseToTextPage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/pages/snapshots/configPage.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/release/snapshots/releaseForm.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/prescription-summary/snapshots/prescriptionSummaryViewEditable.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/claim/snapshots/claimForm.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/snapshots/sorter.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/snapshots/pageHeader.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/tool/site/client/tests/components/snapshots/longRunningTask.spec.tsx.snap | Snapshot update (styled-components class hash changes). |
| packages/fhir-schema-generation/vitest.config.ts | Adds Vitest configuration for the package. |
| packages/fhir-schema-generation/tsconfig.json | Updates TS config for Vitest (types/include/exclude). |
| packages/fhir-schema-generation/tests/parse-simplifier-package.test.ts | Adds tests for parsing + definition file discovery utilities. |
| packages/fhir-schema-generation/tests/index.test.ts | Adds tests around the new schema-generation pipeline entrypoint. |
| packages/fhir-schema-generation/tests/generate-openapi-schema.test.ts | Adds comprehensive tests for schema generation/cardinality/binding enum logic. |
| packages/fhir-schema-generation/tests/fetch-fhir.test.ts | Removes legacy Jest tests for the old downloader implementation. |
| packages/fhir-schema-generation/tests/download-simplifier-package.test.ts | Adds Vitest tests for the new downloader/extractor implementation. |
| packages/fhir-schema-generation/tests/common.test.ts | Adds tests for common helpers (filename normalization). |
| packages/fhir-schema-generation/src/utils/parse-simplifier-package.ts | Adds parsing utilities and definition-file discovery helper. |
| packages/fhir-schema-generation/src/utils/generate-openapi-schema.ts | Introduces schema generation logic (including cardinality + binding-enum handling). |
| packages/fhir-schema-generation/src/utils/download-simplifier-package.ts | Reworks Simplifier package download/extraction and caching behavior. |
| packages/fhir-schema-generation/src/utils/common.ts | Adds filename normalization helper. |
| packages/fhir-schema-generation/src/types/fhir-to-json-schema-type.ts | Adds FHIR primitive → JSON Schema type mapping. |
| packages/fhir-schema-generation/src/types/editable-json-schema.type.ts | Adds mutable JSON Schema type helper. |
| packages/fhir-schema-generation/src/types/deep-mutable.type.ts | Adds generic deep-mutable utility type. |
| packages/fhir-schema-generation/src/models/structure-definition/type.interface.ts | Adds StructureDefinition type model. |
| packages/fhir-schema-generation/src/models/structure-definition/snapshot.interface.ts | Adds snapshot model interface. |
| packages/fhir-schema-generation/src/models/structure-definition/kind.type.ts | Adds StructureDefinition kind union type. |
| packages/fhir-schema-generation/src/models/structure-definition/identity-map.interface.ts | Adds mapping model interface. |
| packages/fhir-schema-generation/src/models/structure-definition/extension.interface.ts | Adds extension model interface. |
| packages/fhir-schema-generation/src/models/structure-definition/differential-element.interface.ts | Adds differential element model interface. |
| packages/fhir-schema-generation/src/models/structure-definition/constraint.interface.ts | Adds constraint model interface. |
| packages/fhir-schema-generation/src/models/structure-definition/base-element.interface.ts | Adds base element model interface. |
| packages/fhir-schema-generation/src/models/fhir/fhir-package-metadata.ts | Removes legacy FHIR package metadata model. |
| packages/fhir-schema-generation/src/models/fhir-package/structure-definition.interface.ts | Adds StructureDefinition model used by the new generator. |
| packages/fhir-schema-generation/src/models/fhir-package/package-version.interface.ts | Adds package-version interface used by the downloader. |
| packages/fhir-schema-generation/src/models/fhir-package/fhir-package-metadata.interface.ts | Adds package metadata interface used by the downloader. |
| packages/fhir-schema-generation/src/models/fhir-package/dist-tags.interface.ts | Adds dist-tags interface used by package metadata. |
| packages/fhir-schema-generation/src/index.ts | Implements the new end-to-end schema generation pipeline entrypoint. |
| packages/fhir-schema-generation/package.json | Switches tests to Vitest and updates devDependencies. |
| packages/fhir-schema-generation/jest.config.ts | Removes Jest config (migrated to Vitest). |
| Makefile | Adds grype-scan-local target and adjusts workspace install ordering. |
Comments suppressed due to low confidence (2)
packages/fhir-schema-generation/src/utils/download-simplifier-package.ts:18
- The leading JSDoc delimiter is malformed (
/** * ...), which breaks standard doc tooling/formatting. Also the doc still describesversionas optional, butqueryPackageVersionnow throws when it’s undefined/empty—update the doc to match current behavior.
packages/fhir-schema-generation/src/utils/download-simplifier-package.ts:140 downloadSimplifierPackagecurrently logsoutputDir/rawOutputFileunconditionally, and the cache branch returns early when the tarball exists without ensuring the extractedpackage/directory is present. This can leave the pipeline in a broken state if the extract folder was cleaned but the tarball remains. Consider removing the debug logs and, when the tarball is cached, either verifyoutputDir/package/package.jsonexists or re-run extraction from the cached tarball.
| "@eslint/js": "^10.0.1", | ||
| "@types/node": "^25.5.2", | ||
| "@types/tar": "^7.0.87", | ||
| "eslint": "^10.2.0", | ||
| "json-schema-to-ts": "^3.1.1", | ||
| "tsx": "^4.21.0", | ||
| "typescript": "^5.9.3", | ||
| "typescript-eslint": "^8.58.0", | ||
| "vitest": "^3.0.0", | ||
| "@vitest/coverage-v8": "^3.0.0" | ||
| }, | ||
| "dependencies": { | ||
| "tar": "^7.5.13" |
There was a problem hiding this comment.
tar is imported/used at runtime (e.g. in download-simplifier-package.ts), but package.json no longer has a dependencies section and lists tar under devDependencies. If this package is installed with --omit=dev (common in CI/production), node dist/index.js will fail at runtime. Move tar (and any other runtime deps) into dependencies, or ensure runtime always installs devDependencies.
| // sets a deeply nested property on an object, creating intermediate objects as needed. | ||
| function setNestedProp( | ||
| obj: Record<string, EditableJSONSchema>, | ||
| propertyPath: Array<string>, | ||
| value: EditableJSONSchema | ||
| ): void { | ||
| const pathTraverse = propertyPath.slice(0, -1) | ||
| const finalKey = propertyPath[propertyPath.length - 1] | ||
|
|
||
| const targetObj = pathTraverse.reduce<Record<string, EditableJSONSchema>>((prev, curr) => { | ||
| if (!prev[curr]) { | ||
| const empty: EditableJSONSchema = {} | ||
| prev[curr] = empty | ||
| } | ||
| return prev[curr] as Record<string, EditableJSONSchema> | ||
| }, obj) |
There was a problem hiding this comment.
setNestedProp builds nested structures by assigning plain objects (and even mutating existing schemas like $ref objects) instead of creating proper JSON Schema objects with type: "object" + properties (or allOf to extend a $ref). In draft-04, siblings of $ref are ignored, so nested fields like initialFill.duration will be dropped/ineffective. Consider generating a sub-definition (e.g. Resource_DispenseRequest_InitialFill) and referencing it, or using allOf: [{$ref: ...}, {type:"object", properties:{...}}] for nested additions.
| if (!definitions[definitionKey]) { | ||
| let baseRef: string | ||
| if (definitionKey === schema.name) { | ||
| baseRef = deriveBaseRef(schema) | ||
| } else { | ||
| const baseClass = element.type[0].code || "BackboneElement" | ||
| baseRef = `${baseClass}#/definitions/${baseClass}` | ||
| } |
There was a problem hiding this comment.
For sub-definitions (e.g. Resource_DispenseRequest), baseRef is derived from element.type[0].code, but for nested elements that type is the leaf field type (e.g. Quantity for ...dispenseRequest.quantity) rather than the container/backbone element type. This makes the generated schema inheritance incorrect. Prefer deriving the base from the structure/backbone element itself (typically BackboneElement), or look up the type from the corresponding container element in the differential (e.g. Resource.dispenseRequest).
| * Reads a JSON Schema file produced by the Simplifier package and returns | ||
| * its definitions types | ||
| * | ||
| * @param directory Root directory of the extracted Simplifier package. |
There was a problem hiding this comment.
The JSDoc for parseSimplifierPackage lists parameters (directory, filePath) that don’t match the actual function signature (only filePath). This makes the API misleading for callers; please update the comment to reflect the real parameters/behavior.
| * Reads a JSON Schema file produced by the Simplifier package and returns | |
| * its definitions types | |
| * | |
| * @param directory Root directory of the extracted Simplifier package. | |
| * its definition types. | |
| * | |
| * @param filePath Path to the Simplifier JSON schema file to read | |
| * (for example "openapi/MedicationRequest.schema.json"). |
| */ | ||
| export async function getSimplifierDefinitionFiles(folderPath: string, prefix: string): Promise<Array<string>> { | ||
| try { | ||
| // Read the directory contents, returning fs.Dirent objects |
There was a problem hiding this comment.
getSimplifierDefinitionFiles is declared async but uses fs.readdirSync (and even awaits it). This is misleading and blocks the event loop. Either make it fully synchronous (remove async/Promise) or switch to await fs.promises.readdir(...) and keep it async.
| // Read the directory contents, returning fs.Dirent objects | |
| const items = await fs.promises.readdir(folderPath, {withFileTypes: true}) |
| function buildOutputPath(): string { | ||
| return path.join( | ||
| process.cwd(), | ||
| ".output", | ||
| "parsed", | ||
| normalizeFileName(`${PACKAGE_NAME}-${PACKAGE_VERSION}`) | ||
| ) | ||
| } |
There was a problem hiding this comment.
buildOutputPath() uses a constant PACKAGE_VERSION = "latest", so successive runs will extract different resolved package versions into the same directory. Because tar extraction won’t remove files that disappeared between versions, stale files can persist and affect schema generation. Consider incorporating the resolved version into the output directory name (after queryPackageVersion), or clearing the existing package/ directory before extracting a new version.
|


Summary
Details
Formatted MedicationRequest (cardinality of definitions doesn't show here because all of them have a default min value of 0, so
"minItem": 0is omitted from body)