-
Notifications
You must be signed in to change notification settings - Fork 78
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
Enable resources to be sent to a generic FHIR store using the hapi client #30
Conversation
4be317d
to
93a1f56
Compare
93a1f56
to
0111f07
Compare
0111f07
to
a21d1e8
Compare
return new GcpStoreUtil(fhirStoreUrl, fhirContext); | ||
else | ||
return new FhirStoreUtil(fhirStoreUrl, fhirContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructors here are defined to take an IGenericClient
rather than FhirContext
, so this should likely be something like:
return new FhirStoreUtil(fhirStoreUrl, fhirContext.newRestfulGenericClient());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, I tend to think that if ... else
should always be blocks:
if (condition) {
} else {
}
This makes it easier if additional actions need to be taken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructors here are defined to take an
IGenericClient
rather thanFhirContext
, so this should likely be something like:return new FhirStoreUtil(fhirStoreUrl, fhirContext.newRestfulGenericClient());
Yeah, I accidentally committed in the middle of refactoring -_- thanks!
@@ -130,7 +138,7 @@ static OpenmrsUtil createOpenmrsUtil(String sourceUrl, String sourceUser, String | |||
throws CannotProvideCoderException { | |||
FhirSearchUtil fhirSearchUtil = createFhirSearchUtil(options, fhirContext); | |||
// TODO remove fhirContext from fhirStoreUtil. | |||
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(options.getGcpFhirStore(), fhirContext); | |||
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(options.getFhirStoreUrl(), fhirContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(options.getFhirStoreUrl(), fhirContext); | |
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(options.getFhirStoreUrl(), fhirContext.newRestfulGenericClient()); |
@@ -194,15 +202,15 @@ public void Setup() { | |||
fhirContext = FhirContext.forDstu3(); | |||
fhirStoreUtil = new FhirStoreUtil(fhirStoreUrl, fhirContext); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fhirStoreUtil = new FhirStoreUtil(fhirStoreUrl, fhirContext); | |
fhirStoreUtil = new FhirStoreUtil(fhirStoreUrl, fhirContext.newRestfulGenericClient()); |
d5f2565
to
c17997b
Compare
@pmanko Thanks for the great work. would think it would be useful at some point dealing with non FHIR data . What do you think |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pmanko for this change; I like the unified interface of FhirStoreUtil
. Have you tested your changes on a GCP FHIR store too?
options.getPassword(), fhirContext)); | ||
} | ||
|
||
static FhirStoreUtil createFhirStoreUtil(String fhirStoreUrl, FhirContext fhirContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this functionality is needed elsewhere too (e.g., in the streaming pipelines), so here is a suggestion: Create a factory method somewhere under common
where internally it uses the pattern matching logic of matchesGcpPattern
but do not expose the metchesGcpPattern
function publicly.
BTW, please update all usages of FhirStoreUtil
in the code; I am thinking that the constructor for FhirStoreUtil
should be private and instances of that should only be created through the factory method, WDYT?
private OpenmrsUtil openmrsUtil; | ||
|
||
FhirSearchUtil(OpenmrsUtil openmrsUtil) { | ||
FhirSearchUtil(FhirStoreUtil fhirStoreUtil, OpenmrsUtil openmrsUtil) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert this change; the dependency between FhirSearchUtil
and FhirStoreUtil
was intentionally removed in this commit. The idea is that, FhirStoreUtil
is any functionality for uploading data to a FHIR-store and the only reason we needed FhirStoreUtil
here was uploadBundleToCloud
for which the right place is actually FhirStoreUtil
itself.
@@ -79,4 +84,12 @@ public String findBaseSearchUrl(Bundle searchBundle) { | |||
} | |||
} | |||
|
|||
public MethodOutcome uploadBundleToCloud(Bundle bundle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said above, this was intentionally moved to FhirStoreUtil
, let's keep it that way.
@@ -192,17 +201,17 @@ static OpenmrsUtil createOpenmrsUtil(String sourceUrl, String sourceUser, String | |||
@Setup | |||
public void Setup() { | |||
fhirContext = FhirContext.forDstu3(); | |||
fhirStoreUtil = new FhirStoreUtil(fhirStoreUrl, fhirContext); | |||
fhirStoreUtil = new FhirStoreUtil(fhirStoreUrl, fhirContext.getRestfulClientFactory()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The above createFhirStoreUtil
has a conditional logic for creating FhirStoreUtil
(depending on flag values), shouldn't we have the same pattern here? I mean does this work if the fhirStoreUril is a GCP FHIR-store?
hibernate.connection.username=root | ||
hibernate.connection.password=test | ||
hibernate.connection.username=admin | ||
hibernate.connection.password=BHPmyMtaLj6S |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these are not standard in OpenMRS, let's revert these (to prevent the impression that there is naything specific with these).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bashir2 should we use a .templates
files / another approach to more easily allow individual user settings to be kept as a hibernate.default.properties
file without checking it into github?
hibernate.connection.driver_class=com.mysql.jdbc.Driver | ||
hibernate.connection.url=jdbc:mysql://localhost:3306/atomfeed_client?autoReconnect=true | ||
hibernate.connection.url=jdbc:mysql://openmrs-hapi-db.chijnl5rwr2c.eu-central-1.rds.amazonaws.com:3306/atomfeed_client?autoReconnect=true&useSSL=false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto, I feel it is better to keep localhost
as the default.
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(System.getProperty("cloud.gcpFhirStore"), fhirContext); | ||
IParser parser = fhirContext.newJsonParser(); | ||
return new FhirConverter(openmrsUtil, fhirStoreUtil, parser); | ||
FhirStoreUtil fhirStoreUtil = new FhirStoreUtil(System.getProperty("cloud.gcpFhirStore"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one should be conditional too; also change the property name from cloud.gcpFhirStore
to something more generic.
// https://github.com/GoogleCloudPlatform/java-docs-samples/healthcare/tree/master/healthcare/v1 | ||
// TODO: remove redundant resource information if passing a HAPI resource | ||
public void uploadResourceToCloud(String resourceType, String resourceId, Resource resource) { | ||
public MethodOutcome uploadResourceToCloud(Resource resource) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's drop ToCloud
from this method name as it is now more generic.
74a77c7
to
ccf7807
Compare
c0909f9
to
690b080
Compare
@pmanko I'm all for dropping the |
that makes sense @ibacher |
Great work @pmanko! I think we should have an alpha version of the entire pipeline once this PR is merged! Do we have a FHIR store container/docker-compose that I can test it out? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates @pmanko; comments are mostly minor points.
Also can you please add a TESTED field to your PR description. I am actually thinking of creating a template for PR descriptions including a TESTED field because given that we don't have an e2e test yet and three major way of running pipelines, it is good to know what e2e tests have been done manually or not.
@@ -29,6 +29,8 @@ | |||
|
|||
private static final Logger log = LoggerFactory.getLogger(FhirSearchUtil.class); | |||
|
|||
FhirStoreUtil fhirStoreUtil; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is needed anymore, is it?
@@ -14,7 +14,7 @@ | |||
package org.openmrs.analytics; | |||
|
|||
import static org.hamcrest.MatcherAssert.assertThat; | |||
import static org.hamcrest.Matchers.equalTo; | |||
import static org.hamcrest.Matchers.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid wildcard imports: style guide.
@@ -51,6 +52,9 @@ | |||
@Mock | |||
private OpenmrsUtil openmrsUtil; | |||
|
|||
@Mock | |||
private FhirStoreUtil fhirStoreUtil; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be needed either.
@@ -66,6 +70,8 @@ | |||
|
|||
private FhirSearchUtil fhirSearchUtil; | |||
|
|||
private MethodOutcome outcome; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
private static final NetHttpTransport HTTP_TRANSPORT = new NetHttpTransport(); | ||
|
||
private HttpUtil httpUtil; | ||
FhirStoreUtil(String sinkUrl, IRestfulClientFactory clientFactory) throws IllegalArgumentException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this private
?
I would say let's remove it if it is not used anywhere; we can always resurrect it from the history if it is needed in the future. |
updateFhirResource(sinkUrl, resource); | ||
} | ||
catch (Exception e) { | ||
System.out.println(String.format("Exception while sending to sink: %s", e.toString())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use log.error
.
a1bb7ed
to
0d4804c
Compare
4cd0bdb
to
ac99269
Compare
oh sure @pmanko , thats ok |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pmanko for the updates. From your other comments it seems that you are not done with the e2e tests yet but I went ahead and took another look to speed up the review process. It would be great if you can finish this soon as I think this is blocking @mozzy11 (please also update the PR description with a TESTED field).
} | ||
} | ||
|
||
public MethodOutcome uploadResource(Resource resource) { | ||
Collection<IClientInterceptor> interceptors = Collections.<IClientInterceptor> emptyList(); | ||
|
||
if (sinkUsername.isEmpty() && !sinkPassword.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a !
is missing from the first condition, right?
@@ -74,10 +92,10 @@ public MethodOutcome uploadResource(Resource resource) { | |||
return responses; | |||
} | |||
|
|||
protected MethodOutcome updateFhirResource(String sinkUrl, Resource resource, | |||
protected MethodOutcome updateFhirResource(String targetUri, Resource resource, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Is there a particular reason to call this "target" here but "sink" everywhere else? I don't mind either of these choices but I rather be consistent.
throw new IllegalArgumentException( | ||
String.format("The gcpFhirStore %s does not match Google cloud fhir dataset pattern!", gcpFhirStore)); | ||
} | ||
protected GcpStoreUtil(String sinkUrl, String sinkUsername, String sinkPassword, IRestfulClientFactory clientFactory) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think BasicAuth makes sense for the GCP FHIR store and regardless we are not using it here. So I suggest that you drop username/password from the constructor signature here to make it more clear and prevent confusion.
Communication with a FHIR-compliant endpoint - like a HAPI FHIR JPA server or Google Healthcare APIs - should follow the specifications of the FHIR standard, and as a result can be facilitated by leveraging a FHIR client implementation like the [HAPI client](https://hapifhir.io/hapi-fhir/docs/client/introduction.html). This commit updates the communication with target FHIR repositories to use this client library.
1860c6e
to
aa2898f
Compare
aa2898f
to
4597929
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @pmanko for the tests; LGTM. Just two minor points/questions.
public MethodOutcome uploadResource(Resource resource) { | ||
Collection<IClientInterceptor> interceptors = Collections.<IClientInterceptor> emptyList(); | ||
|
||
if (sinkUsername != null && !sinkUsername.isEmpty() && sinkPassword != null && !sinkPassword.isEmpty()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These cannot be null
can they? Also isNullOrEmpty
might be handy.
} | ||
catch (URISyntaxException e) { | ||
log.error(String.format("URI syntax exception while using Google APIs: %s", e.toString())); | ||
catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we expecting any exceptions here? Or put it another way: Are you trying to catch all runtime exceptions here? If yes, why? I think it is a better practice to only catch checked exceptions.
3da71fc
to
0e05cf4
Compare
Closes #25 and #29.
TESTED
mvn exec:java -pl streaming-atomfeed -Dexec.mainClass=org.openmrs.analytics.FhirStreaming -Dexec.args="http://18.158.139.243:8091/openmrs admin/Admin123 http://18.158.139.243:8098/hapi-fhir-jpaserver/fhir"
Some fixes based on these tests were put into this PR, while other will be added as issues.
See: https://github.com/pmanko/openmrs-fhir-analytics/tree/25-hapi-client-to-sink-endtoend