Digital Tooling Implementation Guide
0.0.6 - release
Digital Tooling Implementation Guide - Local Development build (v0.0.6) 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.
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.ig: 0.0.5
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.
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 behaviour is defined for each case
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.
Case 2: supportedProfile
defined
* rest.resource[+].type = #Patient
* rest.resource[=].supportedProfile[+] = Canonical(ExamplePatientProfile)
* rest.resource[=].interaction[+].code = #read
An OpenAPI schema will be generated for the R4 Patient schema, AND a custom schema for the ExamplePatientProfile
. This is annotated using anyOf
. Multiple supportedProfile
are supported.
Case 3: profile
defined
* rest.resource[+].type = #Patient
* rest.resource[=].profile = Canonical(ExamplePatientProfile)
* rest.resource[=].interaction[+].code = #read
An OpenAPI schema will be generated for the ExamplePatientProfile
. The base R4 Patient resource schema will not be generated.
Case 4: profile
and supportedProfile
defined
* rest.resource[+].type = #Patient
* rest.resource[=].profile = Canonical(ExamplePatientProfile)
* rest.resource[=].supportedProfile = Canonical(ExamplePatientProfile2)
* rest.resource[=].interaction[+].code = #read
An OpenAPI schema will be generated for the ExamplePatientProfile
and ExamplePatientProfile2
. This is annotated using anyOf
.
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.