use of org.hl7.fhir.definitions.model.Operation in project gpconnect-demonstrator by nhsconnect.
the class GpConnectServerCapabilityStatementProvider method getServerConformance.
@Override
public CapabilityStatement getServerConformance(HttpServletRequest theRequest) {
final boolean FROM_JSON = true;
CapabilityStatement capabilityStatement = null;
if (!FROM_JSON) {
// Get the automatically generated statement
capabilityStatement = super.getServerConformance(theRequest);
// added at 1.2.2 force overwrite the default entries which are not correct
for (CapabilityStatement.CapabilityStatementRestComponent st : capabilityStatement.getRest()) {
for (CapabilityStatement.CapabilityStatementRestOperationComponent operation : st.getOperation()) {
String opPlusDollar = "$" + operation.getName();
switch(opPlusDollar) {
case REGISTER_PATIENT_OPERATION_NAME:
operation.getDefinition().setReference(OD_GPC_REGISTER_PATIENT);
break;
case GET_STRUCTURED_RECORD_OPERATION_NAME:
operation.getDefinition().setReference(OD_GPC_GET_STRUCTURED_RECORD);
break;
}
}
}
} else {
try {
// 1.2.6 #316
String interactionId = theRequest.getHeader(SSP_INTERACTIONID);
String capabilityFile = null;
switch(interactionId) {
case REST_READ_STRUCTURED_METADATA:
capabilityFile = "structured_capability.json";
break;
default:
capabilityFile = "capability.json";
}
// read a json capability file
String capabilityJson = new String(Files.readAllBytes(Paths.get(FhirRequestGenericIntercepter.getConfigPath() + "/" + capabilityFile)));
FhirContext ctx = FhirContext.forDstu3();
capabilityStatement = (CapabilityStatement) ctx.newJsonParser().parseResource(capabilityJson);
} catch (IOException ex) {
return null;
}
}
// And add additional required information
capabilityStatement.setVersion(SystemVariable.VERSION);
capabilityStatement.setDescription("This server implements the GP Connect API version " + SystemVariable.VERSION);
capabilityStatement.setName("GP Connect");
capabilityStatement.setCopyright("Copyright NHS Digital 2018");
capabilityStatement.getSoftware().setReleaseDate(Date.valueOf(LocalDate.parse("2017-09-27")));
return capabilityStatement;
}
use of org.hl7.fhir.definitions.model.Operation in project gpconnect-demonstrator by nhsconnect.
the class PatientResourceProvider method registerPatient.
@Operation(name = REGISTER_PATIENT_OPERATION_NAME)
public Bundle registerPatient(@ResourceParam Parameters params) {
Patient registeredPatient = null;
validateParameterNames(params, registerPatientParams);
Patient unregisteredPatient = params.getParameter().stream().filter(param -> "registerPatient".equalsIgnoreCase(param.getName())).map(ParametersParameterComponent::getResource).map(Patient.class::cast).findFirst().orElse(null);
String nnn = nhsNumber.fromPatientResource(unregisteredPatient);
// if its patient 14 spoof not on PDS and return the required error
if (nnn.equals(patientNotOnSpine)) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new InvalidRequestException(String.format("Patient (NHS number - %s) not present on PDS", nnn)), SystemCode.INVALID_PATIENT_DEMOGRAPHICS, IssueType.INVALID);
} else if (nnn.equals(patientSuperseded)) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new InvalidRequestException(String.format("Patient (NHS number - %s) is superseded", nnn)), SystemCode.INVALID_NHS_NUMBER, IssueType.INVALID);
}
if (unregisteredPatient != null) {
validatePatient(unregisteredPatient);
// check if the patient already exists
PatientDetails patientDetails = patientSearch.findPatient(nhsNumber.fromPatientResource(unregisteredPatient));
if (patientDetails == null || IsInactiveTemporaryPatient(patientDetails)) {
if (patientDetails == null) {
patientDetails = registerPatientResourceConverterToPatientDetail(unregisteredPatient);
patientStore.create(patientDetails);
} else {
// reactivate inactive non temporary patient
patientDetails.setRegistrationStatus(ACTIVE_REGISTRATION_STATUS);
updateAddressAndTelecom(unregisteredPatient, patientDetails);
patientStore.update(patientDetails);
}
try {
registeredPatient = patientDetailsToRegisterPatientResourceConverter(patientSearch.findPatient(unregisteredPatient.getIdentifierFirstRep().getValue()));
addPreferredBranchSurgeryExtension(registeredPatient);
} catch (FHIRException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (patientDetails.isDeceased() || patientDetails.isSensitive()) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new InvalidRequestException(String.format("Patient (NHS number - %s) has invalid demographics", nnn)), SystemCode.INVALID_PATIENT_DEMOGRAPHICS, IssueType.INVALID);
} else {
throw OperationOutcomeFactory.buildOperationOutcomeException(new UnclassifiedServerFailureException(409, String.format("Patient (NHS number - %s) already exists", nnn)), SystemCode.DUPLICATE_REJECTED, IssueType.INVALID);
}
} else {
throw OperationOutcomeFactory.buildOperationOutcomeException(new UnprocessableEntityException("Patient record not found"), SystemCode.INVALID_PARAMETER, IssueType.INVALID);
}
Bundle bundle = new Bundle().setType(BundleType.SEARCHSET);
bundle.getMeta().addProfile(SystemURL.SD_GPC_SRCHSET_BUNDLE);
bundle.addEntry().setResource(registeredPatient);
return bundle;
}
use of org.hl7.fhir.definitions.model.Operation in project gpconnect-demonstrator by nhsconnect.
the class PatientResourceProvider method StructuredRecordOperation.
@Operation(name = GET_STRUCTURED_RECORD_OPERATION_NAME)
public Bundle StructuredRecordOperation(@ResourceParam Parameters params) throws FHIRException {
Bundle structuredBundle = new Bundle();
Boolean getAllergies = false;
Boolean includeResolved = false;
Boolean getMedications = false;
Boolean includePrescriptionIssues = false;
Period medicationPeriod = null;
String NHS = getNhsNumber(params);
PatientDetails patientDetails = patientSearch.findPatient(NHS);
// see https://nhsconnect.github.io/gpconnect/accessrecord_structured_development_retrieve_patient_record.html#error-handling
if (patientDetails == null || patientDetails.isSensitive() || patientDetails.isDeceased() || !patientDetails.isActive()) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new ResourceNotFoundException("No patient details found for patient ID: " + NHS), SystemCode.PATIENT_NOT_FOUND, IssueType.NOTFOUND);
}
if (NHS.equals(patientNoconsent)) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new ForbiddenOperationException("No patient consent to share for patient ID: " + NHS), SystemCode.NO_PATIENT_CONSENT, IssueType.FORBIDDEN);
}
operationOutcome = null;
for (ParametersParameterComponent param : params.getParameter()) {
if (validateParametersName(param.getName())) {
if (param.getName().equals(SystemConstants.INCLUDE_ALLERGIES)) {
getAllergies = true;
if (param.getPart().isEmpty()) {
// addWarningIssue(param, IssueType.REQUIRED, "Miss parameter part : " + SystemConstants.INCLUDE_RESOLVED_ALLERGIES);
throw OperationOutcomeFactory.buildOperationOutcomeException(new UnprocessableEntityException("Miss parameter : " + SystemConstants.INCLUDE_RESOLVED_ALLERGIES), SystemCode.INVALID_PARAMETER, IssueType.REQUIRED);
}
boolean includeResolvedParameterPartPresent = false;
for (ParametersParameterComponent paramPart : param.getPart()) {
if (paramPart.getName().equals(SystemConstants.INCLUDE_RESOLVED_ALLERGIES)) {
if (paramPart.getValue() instanceof BooleanType) {
includeResolved = Boolean.valueOf(paramPart.getValue().primitiveValue());
includeResolvedParameterPartPresent = true;
} else {
throw OperationOutcomeFactory.buildOperationOutcomeException(new UnprocessableEntityException("Miss parameter : " + SystemConstants.INCLUDE_RESOLVED_ALLERGIES), SystemCode.INVALID_PARAMETER, IssueType.REQUIRED);
}
} else {
addWarningIssue(param, paramPart, IssueType.NOTSUPPORTED);
// throw OperationOutcomeFactory.buildOperationOutcomeException(
// new UnprocessableEntityException("Incorrect parameter passed : " + paramPart.getName()),
// SystemCode.INVALID_PARAMETER, IssueType.INVALID);
}
}
if (!includeResolvedParameterPartPresent) {
throw OperationOutcomeFactory.buildOperationOutcomeException(new UnprocessableEntityException("Miss parameter : " + SystemConstants.INCLUDE_RESOLVED_ALLERGIES), SystemCode.INVALID_PARAMETER, IssueType.REQUIRED);
}
}
if (param.getName().equals(SystemConstants.INCLUDE_MEDICATION)) {
getMedications = true;
boolean isIncludedPrescriptionIssuesExist = false;
for (ParametersParameterComponent paramPart : param.getPart()) {
if (paramPart.getName().equals(SystemConstants.INCLUDE_PRESCRIPTION_ISSUES)) {
if (paramPart.getValue() instanceof BooleanType) {
includePrescriptionIssues = Boolean.valueOf(paramPart.getValue().primitiveValue());
isIncludedPrescriptionIssuesExist = true;
}
} else if (paramPart.getName().equals(SystemConstants.MEDICATION_SEARCH_FROM_DATE) && paramPart.getValue() instanceof DateType) {
DateType startDateDt = (DateType) paramPart.getValue();
medicationPeriod = new Period();
medicationPeriod.setStart(startDateDt.getValue());
medicationPeriod.setEnd(null);
String startDate = startDateDt.asStringValue();
if (!validateStartDateParamAndEndDateParam(startDate, null)) {
// addWarningIssue(param, paramPart, IssueType.INVALID, "Invalid date used");
}
} else {
addWarningIssue(param, paramPart, IssueType.NOTSUPPORTED);
// throw OperationOutcomeFactory.buildOperationOutcomeException(
// new UnprocessableEntityException("Incorrect parameter passed : " + paramPart.getName()),
// SystemCode.INVALID_PARAMETER, IssueType.INVALID);
}
}
if (!isIncludedPrescriptionIssuesExist) {
// # 1.2.6 now defaults to true if not provided
includePrescriptionIssues = true;
}
}
} else {
// invalid parameter
addWarningIssue(param, IssueType.NOTSUPPORTED);
}
}
// for parameter
// Add Patient
Patient patient = patientDetailsToPatientResourceConverter(patientDetails);
if (patient.getIdentifierFirstRep().getValue().equals(NHS)) {
structuredBundle.addEntry().setResource(patient);
}
// Organization from patient
Set<String> orgIds = new HashSet<>();
orgIds.add(patientDetails.getManagingOrganization());
// Practitioner from patient
Set<String> practitionerIds = new HashSet<>();
List<Reference> practitionerReferenceList = patient.getGeneralPractitioner();
practitionerReferenceList.forEach(practitionerReference -> {
String[] pracRef = practitionerReference.getReference().split("/");
if (pracRef.length > 1) {
practitionerIds.add(pracRef[1]);
}
});
if (getAllergies) {
structuredBundle = structuredAllergyIntoleranceBuilder.buildStructuredAllergyIntolerence(NHS, practitionerIds, structuredBundle, includeResolved);
}
if (getMedications) {
structuredBundle = populateMedicationBundle.addMedicationBundleEntries(structuredBundle, patientDetails, includePrescriptionIssues, medicationPeriod, practitionerIds, orgIds);
}
// Add all practitioners and practitioner roles
for (String practitionerId : practitionerIds) {
Practitioner pracResource = practitionerResourceProvider.getPractitionerById(new IdType(practitionerId));
structuredBundle.addEntry().setResource(pracResource);
List<PractitionerRole> practitionerRoleList = practitionerRoleResourceProvider.getPractitionerRoleByPracticionerId(new IdType(practitionerId));
for (PractitionerRole role : practitionerRoleList) {
String[] split = role.getOrganization().getReference().split("/");
orgIds.add(split[1]);
structuredBundle.addEntry().setResource(role);
}
}
// Add all organizations
for (String orgId : orgIds) {
OrganizationDetails organizationDetails = organizationSearch.findOrganizationDetails(new Long(orgId));
Organization organization = organizationResourceProvider.convertOrganizationDetailsToOrganization(organizationDetails);
structuredBundle.addEntry().setResource(organization);
}
structuredBundle.setType(BundleType.COLLECTION);
structuredBundle.getMeta().addProfile(SystemURL.SD_GPC_STRUCTURED_BUNDLE);
if (operationOutcome != null) {
structuredBundle.addEntry().setResource(operationOutcome);
} else {
removeDuplicateResources(structuredBundle);
}
return structuredBundle;
}
use of org.hl7.fhir.definitions.model.Operation in project beneficiary-fhir-data by CMSgov.
the class ExplanationOfBenefitResourceProvider method findByPatient.
/**
* Adds support for the FHIR "search" operation for {@link ExplanationOfBenefit}s, allowing users
* to search by {@link ExplanationOfBenefit#getPatient()}.
*
* <p>The {@link Search} annotation indicates that this method supports the search operation.
* There may be many different methods annotated with this {@link Search} annotation, to support
* many different search criteria.
*
* @param patient a {@link ReferenceParam} for the {@link ExplanationOfBenefit#getPatient()} to
* try and find matches for {@link ExplanationOfBenefit}s
* @param type a list of {@link ClaimType} to include in the result. Defaults to all types.
* @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
* pagination
* @param excludeSamhsa an {@link OptionalParam} that, if <code>"true"</code>, will use {@link
* Stu3EobSamhsaMatcher} to filter out all SAMHSA-related claims from the results
* @param lastUpdated an {@link OptionalParam} that specifies a date range for the lastUpdated
* field.
* @param serviceDate an {@link OptionalParam} that specifies a date range for {@link
* ExplanationOfBenefit}s that completed
* @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
* to parse out pagination values
* @return Returns a {@link Bundle} of {@link ExplanationOfBenefit}s, which may contain multiple
* matching resources, or may also be empty.
*/
@Search
@Trace
public Bundle findByPatient(@RequiredParam(name = ExplanationOfBenefit.SP_PATIENT) @Description(shortDefinition = "The patient identifier to search for") ReferenceParam patient, @OptionalParam(name = "type") @Description(shortDefinition = "A list of claim types to include") TokenAndListParam type, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "excludeSAMHSA") @Description(shortDefinition = "If true, exclude all SAMHSA-related resources") String excludeSamhsa, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, @OptionalParam(name = "service-date") @Description(shortDefinition = "Include resources that completed in the given range") DateRangeParam serviceDate, RequestDetails requestDetails) {
/*
* startIndex is an optional parameter here because it must be declared in the
* event it is passed in. However, it is not being used here because it is also
* contained within requestDetails and parsed out along with other parameters
* later.
*/
String beneficiaryId = patient.getIdPart();
Set<ClaimType> claimTypes = parseTypeParam(type);
Boolean includeTaxNumbers = returnIncludeTaxNumbers(requestDetails);
OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/ExplanationOfBenefit?");
Operation operation = new Operation(Operation.Endpoint.V1_EOB);
operation.setOption("by", "patient");
operation.setOption("types", (claimTypes.size() == ClaimType.values().length) ? "*" : claimTypes.stream().sorted(Comparator.comparing(ClaimType::name)).collect(Collectors.toList()).toString());
operation.setOption("IncludeTaxNumbers", "" + includeTaxNumbers);
operation.setOption("pageSize", paging.isPagingRequested() ? "" + paging.getPageSize() : "*");
operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
operation.setOption("service-date", Boolean.toString(serviceDate != null && !serviceDate.isEmpty()));
operation.publishOperationName();
List<IBaseResource> eobs = new ArrayList<IBaseResource>();
// Optimize when the lastUpdated parameter is specified and result set is empty
if (loadedFilterManager.isResultSetEmpty(beneficiaryId, lastUpdated)) {
return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
}
/*
* The way our JPA/SQL schema is setup, we have to run a separate search for
* each claim type, then combine the results. It's not super efficient, but it's
* also not so inefficient that it's worth fixing.
*/
if (claimTypes.contains(ClaimType.CARRIER))
eobs.addAll(transformToEobs(ClaimType.CARRIER, findClaimTypeByPatient(ClaimType.CARRIER, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.DME))
eobs.addAll(transformToEobs(ClaimType.DME, findClaimTypeByPatient(ClaimType.DME, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.HHA))
eobs.addAll(transformToEobs(ClaimType.HHA, findClaimTypeByPatient(ClaimType.HHA, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.HOSPICE))
eobs.addAll(transformToEobs(ClaimType.HOSPICE, findClaimTypeByPatient(ClaimType.HOSPICE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.INPATIENT))
eobs.addAll(transformToEobs(ClaimType.INPATIENT, findClaimTypeByPatient(ClaimType.INPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.OUTPATIENT))
eobs.addAll(transformToEobs(ClaimType.OUTPATIENT, findClaimTypeByPatient(ClaimType.OUTPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.PDE))
eobs.addAll(transformToEobs(ClaimType.PDE, findClaimTypeByPatient(ClaimType.PDE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (claimTypes.contains(ClaimType.SNF))
eobs.addAll(transformToEobs(ClaimType.SNF, findClaimTypeByPatient(ClaimType.SNF, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
if (Boolean.parseBoolean(excludeSamhsa))
filterSamhsa(eobs);
eobs.sort(ExplanationOfBenefitResourceProvider::compareByClaimIdThenClaimType);
// Add bene_id to MDC logs
TransformerUtils.logBeneIdToMdc(Arrays.asList(beneficiaryId));
return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
}
use of org.hl7.fhir.definitions.model.Operation in project beneficiary-fhir-data by CMSgov.
the class PatientResourceProvider method read.
/**
* Adds support for the FHIR "read" operation, for {@link Patient}s. The {@link Read} annotation
* indicates that this method supports the read operation.
*
* <p>Read operations take a single parameter annotated with {@link IdParam}, and should return a
* single resource instance.
*
* @param patientId The read operation takes one parameter, which must be of type {@link IdType}
* and must be annotated with the {@link IdParam} annotation.
* @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
* to parse out pagination values
* @return Returns a resource matching the specified {@link IdDt}, or <code>null</code> if none
* exists.
*/
@Read(version = false)
@Trace
public Patient read(@IdParam IdType patientId, RequestDetails requestDetails) {
if (patientId == null)
throw new IllegalArgumentException();
if (patientId.getVersionIdPartAsLong() != null)
throw new IllegalArgumentException();
String beneIdText = patientId.getIdPart();
if (beneIdText == null || beneIdText.trim().isEmpty())
throw new IllegalArgumentException();
RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
operation.setOption("by", "id");
// there is another method with exclude list: requestHeader.getNVPairs(<excludeHeaders>)
requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
operation.publishOperationName();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Beneficiary> criteria = builder.createQuery(Beneficiary.class);
Root<Beneficiary> root = criteria.from(Beneficiary.class);
root.fetch(Beneficiary_.skippedRifRecords, JoinType.LEFT);
if (requestHeader.isHICNinIncludeIdentifiers())
root.fetch(Beneficiary_.beneficiaryHistories, JoinType.LEFT);
if (requestHeader.isMBIinIncludeIdentifiers())
root.fetch(Beneficiary_.medicareBeneficiaryIdHistories, JoinType.LEFT);
criteria.select(root);
criteria.where(builder.equal(root.get(Beneficiary_.beneficiaryId), beneIdText));
Beneficiary beneficiary = null;
Long beneByIdQueryNanoSeconds = null;
Timer.Context timerBeneQuery = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_id")).time();
try {
beneficiary = entityManager.createQuery(criteria).getSingleResult();
} catch (NoResultException e) {
throw new ResourceNotFoundException(patientId);
} finally {
beneByIdQueryNanoSeconds = timerBeneQuery.stop();
TransformerUtils.recordQueryInMdc(String.format("bene_by_id.include_%s", String.join("_", (List<String>) requestHeader.getValue(HEADER_NAME_INCLUDE_IDENTIFIERS))), beneByIdQueryNanoSeconds, beneficiary == null ? 0 : 1);
}
// Null out the unhashed HICNs if we're not supposed to be returning them
if (!requestHeader.isHICNinIncludeIdentifiers()) {
beneficiary.setHicnUnhashed(Optional.empty());
}
// Null out the unhashed MBIs if we're not supposed to be returning
if (!requestHeader.isMBIinIncludeIdentifiers()) {
beneficiary.setMedicareBeneficiaryId(Optional.empty());
}
// Add bene_id to MDC logs
TransformerUtils.logBeneIdToMdc(Arrays.asList(beneIdText));
Patient patient = BeneficiaryTransformer.transform(metricRegistry, beneficiary, requestHeader);
return patient;
}
Aggregations