Openemr fhir search (#4349)
commit93c71a76f84910059b00543ee63a698e7e531e95
authorStephen Nielson <stephen@nielson.org>
Tue, 18 May 2021 06:38:45 +0000 (18 02:38 -0400)
committerGitHub <noreply@github.com>
Tue, 18 May 2021 06:38:45 +0000 (17 23:38 -0700)
treee69fb9188f3b5eec74d3ced20001c2c9c7dfde55
parenta76b26dd7afb4521affef0429709db191dcc4d97
Openemr fhir search (#4349)

* Initial FHIR Search implementation.

Built out the FHIR search algorithm and implemented it in
FhirServiceBase and BaseService.

I built the search upon a 3 layer approach.  The FHIR layer that is
aware of FHIR search fields types, FHIR concepts, the OpenEMR Service
layer, and then the database / SQL layer.  I believe I've kept those
layers separate from each other with the bottom layers not making any
calls or dependencies on the top layers.

The FHIR search uses the factory design pattern to build a set of
ISearchField objects that represent the various types of search objects
in both FHIR and now in the OpenEMR service layers.  Currently the type
of search fields that are supported are the String and Date fields which are
almost fully supported with Token kinda working and the others not
supported at this point. The only thing missing in the Date search
type is timezone support which I haven't got a good solution to just
yet.

I also implemented an IPatientCompartmentResourceService that any FHIR
service that touches patient data should implement.  It specifies the
patient field that will bind a FHIR search query into the patient
context.  This will make sure that only a single user's patient data
will be returned when operating within just that patient's context.

I've gone through and added a bunch of tests in the
FhirPatientServiceQueryTest object that deals with the different
modifiers, and comparators that FHIR supports.  I abstracted it away so
that the same kinds of modifiers and comparators (greater than, less
than, etc) can be used in the service classes without knowledge of FHIR.

I built it so we can have composite search fields with nested
heirarchies to support all kinds of search operations that can combine
both unions and intersections of search fields.

There is also a big bug fix as the sqlThrowException operation was only
working on insert/update operations that expected no data back.  I fixed
a bug that prevented the operation from returning data.

* Fix the patient fixtures not cleaning.

* Fix coding standards, full patient search.

Got all of the required us core patient search and the original patient
field searches working and tested in the query search.

Looks like I broke a bunch of unit tests though and I'll have to go
about fixing those.

* Code style fixes, unit test fixes.

Got all of the unit tests back up and running, had to fix a number of
problems with how the search parameters were defined and setup.  Also
fixed some broken tests with the Practitioner service classes.  Put more
logic into the BaseService to handle and be able to override table joins
and selection of data from table joins.

I've partially implemented a very primitive ORM, I keep wondering if itd
be better just to incorporate a pre-existing ORM but it seems like the
project has put those in and ripped them out multiple times so I'm
hedging away from that.

Had to temporarily hard code the Practitioner search values (which we
were doing originally anyways) until I can get the npi:missing stuff
working for a search modifier.  The challenge is that the :missing
identifier overrides the search value to be a boolean true/false which
is going to kill a number of the searchfield data validators.

I'm debating on whether I will under the hood change the type to be a
SearchMissingField class instead of relying on whatever the actual
search type of class should be.  I see pros and cons to that approach.
I'll just have to play around with it and see what I end up with.

Overall this should be a pretty good initial stab at a more robust FHIR
searching solution that will pass ONC requirements.

* Fix php7.3 FHIR date search preg_match bug

php7.3 handles the preg_match criteria different than 7.4+.  Switching
to a !empty clause on each preg_match group that was found resolves this
issue.

* US Core resource endpoints, reference type.

Added a way for each resource to update, map, or change records
retrieved from the data layer.

Added the reference search field type enum.

Added the missing US Core entities and have them return empty responses
for now.

Enabled all of the patient us core entities scopes.

Alphabetized the rest routes for FHIR so we can easily locate the
routes.

* Fix uuid return on Patient Service.

* Initial method stub for reference type creation

* Better logging of Authorization Controller.

* Patient Provenance and US-Core Patient requirements.

Got the Single Patient API ONC Us Core Inferno test suite to pass for
the US Core Patient Profile.  All of the core search criteria is working
as well as the gender, language, and race core extensions.  The
communication is throwing a warning but we use the same data values for
CCDA so I believe this is correct, but the test still passes despite the
warning.

Also implemented the Patient Provenance.  We'll see if the
implementation still works in the future as I'm not sure if our
Provenance record needs to contain EVERY single entity for that
Provenance target... we'll have to see as that could be pretty big at
the organization level.

* Unit test fixes, ONC Patient API implementation.

Got the ONC Patient API implementation working correctly.  Had to fix a
number of unit tests that had broken in the various classes like
CarePlan,Device,DiagnosticReport,Goal,Organization.

Also fixed some code style issues.

Extended the List service to include retrieving records from
list_options table.

* Style fixes, Fix search params.

Had a unit test failing due to the _REWRITE_COMMAND get query string
added the get stuff to the HTTP Request object and exclude the server
openemr get params so we have a clean param list in our API requests.

* Renamed the file to fix psr4 errors.

* Fix PSR12 case statement style problems.

* One last style fix urgh.

* Initial search work for FHIR Allergy Intolerance.

Created a query utils to put common query methods in that can be used by
the project that also throw exceptions and are easier to catch / handle
in the framework (IE returning an operation outcome if something goes
wrong rather than just dieing).

Extended FixtureManager to create fixtures for Allergy Intolerance.  Not
sure I like it as we want something more extensible.

Started the initial underpinnings to support the allergy intolerance
searching with FHIR.  We will be doing a reference search so we will
have that piece supported soon.

* FHIR Reference Field, AllergyIntollerance Search.

Implemented the AllergyIntollerance search on patient.  Also implemented
the initial FHIR reference field.  Wrote unit tests to verify the FHIR
AllergyIntolerance is working properly.

* Finalized AllergyIntolerance and passed ONC tests.

Got the final pieces implemented in the AllergyIntolerance FHIR
resource.

Also fixed a bug in FacilityService with the primary organization query.

Fixed the getOne api signature on a number of the services.

* Fixed unit tests,added logging,query helper method

Added another query helper method.  Fixed the unit tests on allergy
intolerance.  Fixed some other unit tests to support an environment
variable on the admin user for unit tests since jerry's test database
uses a different admin account.

* Fixed lookup codes if codeset is not installed

We had an error on the continuous integration service where the code
sets for allergy-intolerance are not installed.  Fixed it so that it
doesn't throw the error if the codeset is not found.  I'm not sure if
the FHIR unit tests just never checked against snomed codes before, but
I find it strange/bizarre that this never threw errors beforehand.

* Style fixes

* Style fixes

* Fixed multiple value search conditions.

@brady.miller helped identify a problem that was breaking the unit
tests.  Found out that we weren't handling the OR condition on search
parameters that had multiple values.  Fixed the search fields to handle
that properly.

* Force patient binding, fixed missing phone numbers.

Tracked why the patient fixtures would fail the test cases sometimes.
Since the patient is chosen at random in some of the patient fixtures it
was messing up on the phone records.  Found there was a patient that did
not have all of the phone numbers specified.  Fixing that resolves the
test cases that were failing.

Co-authored-by: Stephen Nielson <snielson@discoverandchange.com>
77 files changed:
_rest_routes.inc.php
custom/code_types.inc.php
library/sql.inc
src/Common/Auth/OpenIDConnect/Grant/CustomPasswordGrant.php
src/Common/Auth/OpenIDConnect/Repositories/ScopeRepository.php
src/Common/Database/QueryUtils.php [new file with mode: 0644]
src/Common/Http/HttpRestRequest.php
src/Common/Utils/QueryUtils.php [deleted file]
src/RestControllers/AuthorizationController.php
src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php
src/RestControllers/FHIR/FhirCarePlanRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 64% similarity]
src/RestControllers/FHIR/FhirDeviceRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 64% similarity]
src/RestControllers/FHIR/FhirDiagnosticReportRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 64% similarity]
src/RestControllers/FHIR/FhirDocumentReferenceRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 64% similarity]
src/RestControllers/FHIR/FhirGoalRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 64% similarity]
src/RestControllers/FHIR/FhirMetaDataRestController.php
src/RestControllers/FHIR/FhirOrganizationRestController.php
src/RestControllers/FHIR/FhirProvenanceRestController.php [copied from src/RestControllers/FHIR/FhirAllergyIntoleranceRestController.php with 63% similarity]
src/RestControllers/RestControllerHelper.php
src/Services/AllergyIntoleranceService.php
src/Services/BaseService.php
src/Services/FHIR/FhirAllergyIntoleranceService.php
src/Services/FHIR/FhirConditionService.php
src/Services/FHIR/FhirCoverageService.php
src/Services/FHIR/FhirEncounterService.php
src/Services/FHIR/FhirImmunizationService.php
src/Services/FHIR/FhirLocationService.php
src/Services/FHIR/FhirMedicationRequestService.php
src/Services/FHIR/FhirMedicationService.php
src/Services/FHIR/FhirOrganizationService.php
src/Services/FHIR/FhirPatientService.php
src/Services/FHIR/FhirPersonService.php
src/Services/FHIR/FhirPractitionerRoleService.php
src/Services/FHIR/FhirPractitionerService.php
src/Services/FHIR/FhirProcedureService.php
src/Services/FHIR/FhirProvenanceService.php [new file with mode: 0644]
src/Services/FHIR/FhirServiceBase.php
src/Services/FHIR/FhirUrlResolver.php [new file with mode: 0644]
src/Services/FHIR/IPatientCompartmentResourceService.php [new file with mode: 0644]
src/Services/FacilityService.php
src/Services/InsuranceCompanyService.php
src/Services/ListService.php
src/Services/OrganizationService.php
src/Services/PatientService.php
src/Services/PractitionerService.php
src/Services/Search/BasicSearchField.php [new file with mode: 0644]
src/Services/Search/CompositeSearchField.php [new file with mode: 0644]
src/Services/Search/DateSearchField.php [new file with mode: 0644]
src/Services/Search/FHIRSearchFieldFactory.php [new file with mode: 0644]
src/Services/Search/FhirSearchParameterDefinition.php [new file with mode: 0644]
src/Services/Search/FhirSearchWhereClauseBuilder.php [new file with mode: 0644]
src/Services/Search/ISearchField.php [new file with mode: 0644]
src/Services/Search/ReferenceSearchField.php [new file with mode: 0644]
src/Services/Search/ReferenceSearchValue.php [new file with mode: 0644]
src/Services/Search/SearchComparator.php [new file with mode: 0644]
src/Services/Search/SearchFieldComparableValue.php [new file with mode: 0644]
src/Services/Search/SearchFieldException.php [new file with mode: 0644]
src/Services/Search/SearchFieldStatementResolver.php [new file with mode: 0644]
src/Services/Search/SearchFieldType.php [moved from src/FHIR/FhirSearchParameterType.php with 62% similarity]
src/Services/Search/SearchModifier.php [new file with mode: 0644]
src/Services/Search/SearchQueryFragment.php [new file with mode: 0644]
src/Services/Search/ServiceField.php [new file with mode: 0644]
src/Services/Search/StringSearchField.php [new file with mode: 0644]
src/Services/Search/TableSearchProcessor.php [new file with mode: 0644]
src/Services/Search/TokenSearchField.php [new file with mode: 0644]
src/Services/Search/TokenSearchValue.php [new file with mode: 0644]
tests/Tests/Api/PractitionerApiTest.php
tests/Tests/Fixtures/FixtureManager.php
tests/Tests/Fixtures/allergy-intolerance.json [new file with mode: 0644]
tests/Tests/Fixtures/patients.json
tests/Tests/RestControllers/FHIR/FhirOrganizationRestControllerTest.php
tests/Tests/RestControllers/FHIR/FhirPractitionerRestControllerTest.php
tests/Tests/Services/FHIR/FhirAllergyIntoleranceServiceQueryTest.php [new file with mode: 0644]
tests/Tests/Services/FHIR/FhirPatientServiceMappingTest.php
tests/Tests/Services/FHIR/FhirPatientServiceQueryTest.php
tests/Tests/Services/PractitionerServiceTest.php
tests/Tests/Unit/Common/Acl/AclMainTest.php