[FHIR Terminology Service](https://www.hl7.org/fhir/R4/terminology-service.html) specification describes a set of operations on [CodeSystem](https://www.hl7.org/fhir/R4/codesystem.html), [ValueSet](https://www.hl7.org/fhir/R4/valueset.html) and [ConceptMap](https://www.hl7.org/fhir/R4/conceptmap.html) resources. Among those operations, the following four operations appear to be the most widely adopted ones:
|CodeSystem|ValueSet|
|-|-|
|[$lookup](https://www.hl7.org/fhir/R4/codesystem-operation-lookup.html)
[$validate-code](https://www.hl7.org/fhir/R4/codesystem-operation-validate-code.html)|[$expand](https://www.hl7.org/fhir/R4/valueset-operation-expand.html)
[$validate-code](https://www.hl7.org/fhir/R4/valueset-operation-validate-code.html)|
Developing a partial implementation of the specification has been an effective way to explore the [new FHIR framework](https://docs.intersystems.com/irisforhealth20203/csp/docbook/Doc.View.cls?KEY=HXFHIR_server_intro#HXFHIR_server_arch) introduced in IRIS for Health 2020.1. The [implementation](https://github.com/intersystems-ru/fhir-terminology-service) includes four operations listed above, and supports [read](https://www.hl7.org/fhir/R4/http.html#read) and [search](https://www.hl7.org/fhir/R4/http.html#search) interactions for [CodeSystem](https://www.hl7.org/fhir/R4/codesystem.html) and [ValueSet](https://www.hl7.org/fhir/R4/valueset.html) resources.
It's important to note that the implementation uses plain ObjectScript persistent classes as source terminology tables.
### Installation and Testing with a Sample Strategy
The following list outlines installation and basic testing steps:
1. Install IRIS for Health 2020.1 or newer.
2. Set up a new namespace using ```System Administration``` > ```Configuration``` > ```System Configuration``` > ```Namespaces``` menu of the Portal, or by running the command ```do ##class(HS.HC.Util.Installer).InstallFoundation("")``` in HSLIB namespace. Then import classes from [src/cls](https://github.com/intersystems-ru/fhir-terminology-service/tree/main/src/cls/) and [samples/cls](https://github.com/intersystems-ru/fhir-terminology-service/tree/main/samples/cls/) folders of [intersystems-ru/fhir-terminology-service](https://github.com/intersystems-ru/fhir-terminology-service) GitHub repository.
3. Create a custom FHIR metadata set based on R4 set with additional search parameters defined in [dummy-search-parameters.json](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/src/fhir-search-parameters/dummy-search-parameters.json). This can be done either by using ```##class(HS.FHIRServer.ConsoleSetup).Setup()``` interactive utility or by running the command:
```
do ##class(HS.FHIRServer.Installer).InstallMetadataSet("", "", "HL7v40", $lb(""), 1)
```
* This step is required in order for ```$expand``` and ```$validate-code``` operations to support HTTP GET requests.
* Note that FHIR metadata set files packaged with InterSystems IRIS reside in ```/dev/fhir/fhir-metadata``` directory.
4. Create a new FHIR endpoint based on the new metadata set and on [Sample.iscru.fhir.fts.SimpleStrategy](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/samples/cls/Sample/iscru/fhir/fts/SimpleStrategy.cls) class. Again, this can be achieved either by using the interactive utility or by running the command:
```
do ##class(HS.FHIRServer.Installer).InstallInstance("", "Sample.iscru.fhir.fts.SimpleStrategy", "")
```
5. Allow unauthenticated access to the new endpoint: use the interactive utility or run the following [commands](https://docs.intersystems.com/irisforhealth20203/csp/docbook/Doc.View.cls?KEY=HXFHIR_server_install#HXFHIR_server_install_program_configure):
```
set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint("")
set config = strategy.GetServiceConfigData()
set config.DebugMode = 4
do strategy.SaveServiceConfigData(config)
```
6. Populate [Sample.iscru.fhir.fts.model.CodeTable](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/samples/cls/Sample/iscru/fhir/fts/model/CodeTable.cls):
```
do ##class(Sample.iscru.fhir.fts.model.CodeTable).Populate(10)
```
7. Import [fhir-terminology-service.postman_collection.json](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/tests/postman/fhir-terminology-service.postman_collection.json) file into Postman, adjust ```url``` variable defined within the collection, and test the service against ```Sample.iscru.fhir.fts.model.CodeTable``` which is a simple persistent class.
### Supported FHIR Interactions
Currently the only supported search parameter for both CodeSystem and ValueSet is ```url```.
Both HTTP GET and HTTP POST methods are supported for the four operations listed above.
The table below lists some of the possible HTTP GET requests against [Sample.iscru.fhir.fts.model.CodeTable](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/samples/cls/Sample/iscru/fhir/fts/model/CodeTable.cls) class.
| URI
(to be prepended with ```http://:```) | Description |
|----------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| /metadata | Get endpoint's Capability Statement resource. |
| /CodeSystem/Sample.iscru.fhir.fts.model.CodeTable | Read CodeSystem resource corresponding to Sample.iscru.fhir.fts.model.CodeTable class.|
| /ValueSet/Sample.iscru.fhir.fts.model.CodeTable | Read ValueSet resource corresponding to Sample.iscru.fhir.fts.model.CodeTable class. |
| /CodeSystem?url=urn:CodeSystem:CodeTable | Search CodeSystem resource by url. |
| /CodeSystem | Output all avaliable CodeSystem resources. |
| /ValueSet?url=urn:ValueSet:CodeTable | Search ValueSet resource by url. |
| /ValueSet | Output all avaliable ValueSet resources. |
| /CodeSystem/$lookup?system=urn:CodeSystem:CodeTable&code=TEST | Given system and code, get all the details about the concept. |
| /ValueSet/$expand?url=urn:ValueSet:CodeTable | Expand the ValueSet. |
| /CodeSystem/Sample.iscru.fhir.fts.model.CodeTable/$validate-code?code=TEST | Validate that a code is in the code system. |
### Creating a Custom Strategy
In order to expose your own persistent classes as FHIR code systems/value sets, you would need to create your custom strategy class by subclassing [iscru.fhir.fts.FTSStrategy](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/src/cls/iscru/fhir/fts/FTSStrategy.cls), and then create FHIR endpoint based on the new custom strategy (see above installation step #4).
One class parameter and at least three methods must be overridden by your strategy class:
* ```StrategyKey``` class parameter should be assigned some unique value. Name of the current class seems to be a good option.
* ```getCodeTablePackage()``` class method should return package name for a given code system (or value set) identified by its [canonical URL](https://www.hl7.org/fhir/R4/resource.html#canonical). Typically all terminology classes belong to one package, so this method would usually return one and the same package name regardless of argument values.
* ```getCodePropertyName()``` and ```getDisplayPropertyName()``` class methods should return names of class properties that correspond to ```code``` and ```display``` concept elements. Different classes may have different properties mapped to terminology code/display elements.
Other methods and parameters of [iscru.fhir.fts.FTSStrategy](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/src/cls/iscru/fhir/fts/FTSStrategy.cls) that you might find appropriate to override are as follows:
* ```listCodeTableClasses()``` class method needs to be overridden in order to support search requests that result in returning all available code systems (or value sets). This method is supposed to return a list of class names of all available terminology classes. [Sample.iscru.fhir.fts.SimpleStrategy](https://github.com/intersystems-ru/fhir-terminology-service/blob/main/samples/cls/Sample/iscru/fhir/fts/SimpleStrategy.cls) contains the following basic implementation of this method:
```
/// Returns a list of all available code table classes.
ClassMethod listCodeTableClasses() As %List
{
#dim sql As %String = "SELECT name FROM %Dictionary.ClassDefinition WHERE name LIKE '" _ ..#codeTablePACKAGE _ ".%' ORDER BY name"
#dim resultSet As %SQL.StatementResult = ##class(%SQL.Statement).%ExecDirect(, sql)
if (resultSet.%SQLCODE '= 0) && (resultSet.%SQLCODE '= 100) $$$ThrowStatus($$$ERROR($$$SQLError, resultSet.%SQLCODE, resultSet.%Message))
#dim return As %List = ""
while resultSet.%Next()
{
set return = return _ $lb(resultSet.name)
}
quit return
}
```
* ```isExcludedProperty()``` class method has to be overridden if any particular properties of your persistent classes should not show up in CodeSystem resources. By default, this method filters ```Collection```, ```Identity```, ```Internal```, ```MultiDimensional``` and ```Private``` properties out. Note that object reference and stream properties are currently not supported and ignored by the framework.
* ```codeSystemUrlPREFIX``` and ```valueSetUrlPREFIX``` class parameters and ```getCodeSystemForClassname()```, ```getValueSetForClassname()```, ```determineCodeTableClassname()``` and ```determineCodeSystemForValueSet()``` methods control how class names are mapped to [canonical URLs](https://www.hl7.org/fhir/R4/resource.html#canonical) and vice versa. By default, the following naming scheme is used for canonical URLs:
|CodeSystem|ValueSet|
|-|-|
|```urn:CodeSystem:```|```urn:ValueSet:```|
Note that [logical id](https://www.hl7.org/fhir/R4/resource.html#id) (aka server id) of a CodeSystem/ValueSet resource equals full name of its corresponding class.
### TO DO
Currently lacking is support for versioning of code systems, concept hierarchies and ```$subsumes``` operation, ConceptMap resource and plenty of other stuff. Ideas and pull requests are very welcome!