Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FHIR terminology import #2610

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 8 additions & 1 deletion js/components/conceptset/import.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ define([
'./import/identifiers',
'./import/sourcecodes',
'./import/conceptset',
'./import/fhir',
],function(
ko,
view,
Expand Down Expand Up @@ -60,6 +61,12 @@ define([
{
title: ko.i18n('components.conceptSet.repository', 'Repository'),
key: 'repository',
},
{
title: ko.i18n('components.conceptSet.fhir', 'FHIR'),
key: 'fhir',
componentName: 'conceptset-list-import-fhir',
componentParams: {...params, appendConcepts: this.appendConcepts}
}
];
}
Expand Down Expand Up @@ -125,4 +132,4 @@ define([
}

return commonUtils.build('conceptset-list-import', ConceptSetImport, view);
});
});
18 changes: 18 additions & 0 deletions js/components/conceptset/import/fhir.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<div>
<div class="heading">
<span data-bind="text: ko.i18n('components.conceptSet.fhirHeading', 'Import a value set from a FHIR terminology server')"></span>
</div>
<label>
<span data-bind="text: ko.i18n('components.conceptSet.fhirServer', 'FHIR server URL')"></span>
<input class="form-control" type="text" name="fhirServer" data-bind="textInput: fhirServer"/>
</label>
<label>
<span data-bind="text: ko.i18n('components.conceptSet.valueSet', 'Value set URI')"></span>
<input class="form-control" type="text" name="valueSet" data-bind="textInput: valueSet"/>
</label>
<concept-add-box params="{
isActive: canAddConcepts,
onSubmit: doImport,
}"></concept-add-box>
<div class="clear"></div>
</div>
51 changes: 51 additions & 0 deletions js/components/conceptset/import/fhir.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
define([
'knockout',
'text!./fhir.html',
'components/Component',
'./ImportComponent',
'utils/AutoBind',
'utils/Clipboard',
'utils/CommonUtils',
'services/VocabularyProvider',
'services/http',
'appConfig',
], function(
ko,
view,
Component,
ImportComponent,
AutoBind,
Clipboard,
commonUtils,
vocabularyApi,
httpService,
config
){

class FhirImport extends AutoBind(ImportComponent(Component)) {
constructor(params) {
super(params);
this.fhirServer = ko.observable(config.fhirTerminologyUrl);
this.valueSet = ko.observable("");
this.appendConcepts = params.appendConcepts;
this.canAddConcepts = ko.pureComputed(() =>
this.fhirServer().length > 0 &&
this.valueSet().length > 0 &&
params.canEdit()
);
this.doImport = this.doImport.bind(this);
}

async runImport(options) {
const expandUrl = `${this.fhirServer()}/ValueSet/$expand`,
expandParams = {
url: this.valueSet()
}, result = await httpService.fhirService.doGet(expandUrl, expandParams),
codes = result.data.expansion.contains.map(c => c.code),
{data} = await vocabularyApi.getConceptsByCode(codes);
this.appendConcepts(data, options);
}
}

return commonUtils.build('conceptset-list-import-fhir', FhirImport, view);
});
2 changes: 2 additions & 0 deletions js/config/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,7 @@ define(function () {
}
};

appConfig.fhirTerminologyUrl = "https://r4.ontoserver.csiro.au/fhir";

return appConfig;
});
17 changes: 16 additions & 1 deletion js/services/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ define(function(require, exports) {
const config = require('appConfig');
const sharedState = require('atlas-state');
const { Api:OHDSIApi, STATUS } = require('ohdsi-api');
const JSON_RESPONSE_TYPE = 'application/json';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like it replaced JSON_RESPONSE_TYPE with FHIR_JSON_RESPONSE_TYPE, and I don't think you should remove JSON_RESPONSE_TYPE...can you confirm?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also curious about this change and it looks like JSON_RESPONSE_TYPE is already defined in the base class here: https://github.com/OHDSI/UiToolbox/blob/master/src/api/index.js#L15. So I think it is safe to remove.

const FHIR_JSON_RESPONSE_TYPE = 'application/fhir+json';
const TEXT_RESPONSE_TYPE = 'text/plain';
const EventBus = require('services/EventBus');

Expand Down Expand Up @@ -100,6 +100,18 @@ define(function(require, exports) {
}
}

class FhirApi extends Api {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think FhirApi should be a nested class in http.js. The http.js is a module which facilitates http communication. I think the FHIR class handles communicating to a FHIR service. We have a separate folder of services which you can implement your class there, and then you can import the class in the javascript compoennt you want to use (via the define())

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: within this file we do define 2 classes: Api and PlainTextApi. I agree with moving this to another file but noting that we may want to do that for the PlainTextApi as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies, my understanding about the http.js module was that it just wrapped some lower-level HTTP calls to handle passing specific headers, but not something that made some kind of 'client api' to a specific type of service (like the FHIR one or the PlainText one)...I'm not sure what the design decision was about introducing the PlainText api into http.js but it seems that whoever put that forward thought that you would put these type of API clients directly into the http.js module...so, I can't argue with that, so my only comment then is how the JSON_RESPONSE_TYPE was removed....although not sure where that is being used (but possibly it's a constant that is refernced elsewhere in the app).

getHeaders(requestUrl) {
return {
'Accept': FHIR_JSON_RESPONSE_TYPE,
};
}

parseResponse(json) {
return JSON.parse(json);
}
}

const singletonApi = new Api();
singletonApi.setAuthTokenHeader('Authorization');

Expand All @@ -108,7 +120,10 @@ define(function(require, exports) {
plainTextService.setUnauthorizedHandler(() => singletonApi.handleUnauthorized());
plainTextService.setUserTokenGetter(() => singletonApi.getUserToken());

const fhirService = new FhirApi();

singletonApi.plainTextService = plainTextService;
singletonApi.fhirService = fhirService;

return singletonApi;
});