Digital Tooling Implementation Guide
0.0.11 - release
Digital Tooling Implementation Guide - Local Development build (v0.0.11) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions
The OpenAPI Converter tool is a project developed by Te Whatu Ora Health New Zealand, which can generate an Implementation Guide package into a OpenAPI specification, for use by developers consuming FHIR APIs as well as programmatic validation tools such as API Gateways. The source code for the tool is available on GitHub.
In order to meet HNZ Api Publishing Standards, the tool requires an IG to contain a CapabilityStatement which meets the requirements of the the HnzToolingCapabilityStatement
profile. This profile mandates that certain elements are provided such as license URLs, and the ability to add extensions for additional functionality such as request headers, which are not currently documented in a standard CapabilityStatement
resource.
To use the OpenAPI Converter tool, a CapabilityStatement
that is an InstanceOf
the HnzToolingCapabilityStatement
profile resource must be created within an IG package.
Add SUSHI dependency on the Digital Tooling package:
dependencies:
tewhatuora.digitaltooling: 0.0.11 (Use latest version in the package registry https://packages2.fhir.org/packages/tewhatuora.digitaltooling)
Example fsh to create a CapabilityStatement instance
Instance: ExampleCapabilityStatement
InstanceOf: HnzToolingCapabilityStatement
Usage: #definition
The author must provide all of the required fields which are required by the HnzToolingCapabilityStatement
profile, in order to generate a valid specification which in line with the Te Whatu Ora Health New Zealand API Publishing standards (the SUSHI tool will report errors in conformance to the required CapabilityStatement profile).
For a full example, view the ExampleCapabilityStatement fsh source file, or the Example Profile page.
The OpenAPI Converter tool takes an input of a FHIR Implementation Guide package and uses the profiled CapabilityStatement
resource to form an OpenAPI v3 specification in accordance to the Health New Zealand API Publishing standards.
The OpenAPI specifications generated by this tool MUST NOT be considered a full representation of the FHIR interace, as some simplifications are made to the schema. This should be used alongside application level validation performed by a tool such as the FHIR Validator.
Implementers using this tool must annotate their API using the CapabilityStatement
resources effectively to best benefit from the tool.
For each FHIR resource annotated, an OpenAPI path is created, based on the resource interactions listed in the CapabilityStatement
Example FSH to OpenAPI Path mapping containing all REST operations:
FSH interaction | OpenAPI Path and Operation |
---|---|
* rest.resource[=].interaction[+].code = #read |
GET /Patient/{rid} |
* rest.resource[=].interaction[+].code = #vread |
GET /Patient/{rid}/_history/{vid} |
* rest.resource[=].interaction[+].code = #search-type |
GET /Patient |
* rest.resource[=].interaction[+].code = #create |
POST /Patient |
* rest.resource[=].interaction[+].code = #update |
PUT /Patient/{rid} |
* rest.resource[=].interaction[+].code = #patch |
PATCH /Patient/{rid} |
* rest.resource[=].interaction[+].code = #delete |
DELETE /Patient/{rid} |
For each FHIR resource defined, the below use cases will determine which OpenAPI schemas are generated when using this tool.
Use Case 1: no profile
or supportedProfile
defined:
* rest.resource[+].type = #Patient
* rest.resource[=].interaction[+].code = #read
An OpenAPI schema will be generated using the R4 Patient schema. This should be used if your API only supports the base fhir resource with no particular profiles.
Case 2: supportedProfile
or profile
defined
* rest.resource[+].type = #Patient
* rest.resource[=].profile = Canonical(Patient)
* rest.resource[=].supportedProfile[+] = Canonical(ExamplePatientProfile)
* rest.resource[=].interaction[+].code = #read
If supportedProfile
or profile
are defined, schemas will be generated for each of those profiles defined. If it is desired to generate a base for the base profile, the base FHIR canonical URL for the resource should be used, e.g. Canonical(Patient)
. If the base fhir profile is not supported, this should not be present for either profile
or any supportedProfiles
.
Once the schemas are generated, they are annotated in the OpenAPI operations based on the operation type, for example GET requests will have the schemas annotated in responses, and requests with a requestBody will have the schemas associated.
Where an OpenAPI schema is created from a StructureDefinition
resource in the IG, the following functionality is supported:
patternCoding
(CodeableConcepts) as enum valuespatternBoolean
as an enum valuemaxItems
for array items where the StructureDefinition.max
cardinality is definedminItems
for array items where the StructureDefinition.mix
cardinality is definedWhere custom operations are annotated for the system or type, an OpenAPI path is created for the custom operation. Where the operation mode is query
, a GET /${operation}
endpoint will be created. Where the operation mode is operation
, a POST /${operation}
endpoint is created. The parameters defined in the custom operation will be annotated in the OpenAPI spec as either query parameters if it is a GET resource, or a FHIR Parameters
resource in a requestBody for a POST resource.
Case 1: Define a system level operation
* rest.operation[+].name = "summary"
* rest.operation[=].definition = Canonical(ExampleSystemOperationDefinition)
Case 2: Define a type level operation
// Patient resource
* rest.resource[+].type = #Patient
* rest.resource[=].operation[+].name = "summary"
* rest.resource[=].operation[=].definition = Canonical(ExampleQueryOperationDefinition)
* rest.resource[=].operation[+].name = "match"
* rest.resource[=].operation[=].definition = Canonical(ExampleOperationModeOperationDefinition)
Where an API requires HTTP headers to be provided, these can be annotated using an extension on the HnzToolingCapabilityStatement
resource. These are added to all API operations.
Example fsh:
* extension[HnzApiSpecBuilderExtension].extension[globalHeaders].extension[+].url = Canonical(HnzCustomHeadersExtension)
* extension[HnzApiSpecBuilderExtension].extension[globalHeaders].extension[=].extension[key].valueString = "Correlation-Id"
* extension[HnzApiSpecBuilderExtension].extension[globalHeaders].extension[=].extension[value].valueUri = "https://raw.githubusercontent.com/tewhatuora/schemas/main/fhir-definitions-oas/uuid-definition.json"
* extension[HnzApiSpecBuilderExtension].extension[globalHeaders].extension[=].extension[required].valueBoolean = true
For each header, a key valueString
is provided for the header name, and a valueUri
is provided for the header value. This must be a resolveable uri to an OpenAPI schema defining the value. The required valueBoolean
defines whether or not this is listed as a required or optional header.
Where a schema is created using a StructureDefinition
resource, if there are examples contained within the IG using this profile, the examples be added as OpenAPI examples to the OpenAPI specification.
Where the CapabilityStatement
is annotated using the R4 Capability Statement Capabilities extension and oAuth uris extension, the OpenAPI operations will be annotated with the appropriate SMART on FHIR scopes.
Case 1: System to System:
Given the below FSH, which defines SMART on FHIR system to system security
* rest.security.service = #SMART-on-FHIR
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"
* rest.security.extension[=].extension[+].url = "token"
* rest.security.extension[=].extension[=].valueUri = "https://auth.example.com/oauth2/token"
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/capabilities"
* rest.security.extension[=].valueCode = #client-confidential-symmetric
Each standard REST operation in the specification will be annotated with the appropriate SMART on FHIR scope, for example, a POST /Patient
endpoint would be annotated with the system/Patient.c
scope, and an OpenAPI security scheme will be generated.
Case 2: User and Patient:
* rest.security.service = #SMART-on-FHIR
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"
* rest.security.extension[=].extension[+].url = "token"
* rest.security.extension[=].extension[=].valueUri = "https://auth.example.com/oauth2/token"
* rest.security.extension[=].extension[+].url = "authorize"
* rest.security.extension[=].extension[=].valueUri = "https://auth.example.com/oauth2/authorize"
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/capabilities"
* rest.security.extension[=].valueCode = #permission-user
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/capabilities"
* rest.security.extension[=].valueCode = #permission-patient
Each standard REST operation in the specification will be annotated with the appropriate SMART on FHIR scope, for example, a POST /Patient
endpoint would be annotated with the user/Patient.c
scope and patient/Patient.c
, and an OpenAPI security scheme will be generated.
Case 3: Standard oAuth
* rest.security.service = #oAuth
* rest.security.extension[+].url = "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"
* rest.security.extension[=].extension[+].url = "token"
* rest.security.extension[=].extension[=].valueUri = "https://auth.example.com/oauth2/token"
An OpenAPI security scheme will be generated.