public void CreateReferenceSearch_Valid_HasLogicalId() { var request = NrlsPointerHelper.CreateReferenceSearch(FhirRequests.Valid_Create, "logicalIdA"); Assert.Equal("logicalIdA", request.Id); Assert.Equal(ResourceType.DocumentReference, request.ResourceType); }
public void CreateOrg_Returns_Valid_Search() { var request = NrlsPointerHelper.CreateOrgSearch(FhirRequests.Valid_Create, "TestOrg"); var expected = new List <Tuple <string, string> > { new Tuple <string, string>("identifier", $"{FhirConstants.SystemOrgCode}|TestOrg") }; Assert.Equal(expected, request.QueryParameters, Comparers.ModelComparer <IEnumerable <Tuple <string, string> > >()); }
public async SystemTasks.Task <Resource> ValidateConditionalUpdate(FhirRequest request) { if (!request.Resource.ResourceType.Equals(ResourceType.DocumentReference)) { return(OperationOutcomeFactory.CreateInvalidResource("relatesTo")); } var document = request.Resource as DocumentReference; if (document.RelatesTo == null || document.RelatesTo.Count == 0) { return(null); } var relatesTo = _fhirValidation.GetValidRelatesTo(document.RelatesTo); if (relatesTo.element == null) { return(OperationOutcomeFactory.CreateInvalidResource(relatesTo.issue)); } //Subject already validated during ValidateCreate //relatesTo Identifier already validated during ValidateCreate => validPointer var subjectNhsNumber = _fhirValidation.GetSubjectReferenceId(document.Subject); var pointerRequest = NrlsPointerHelper.CreateMasterIdentifierSearch(request, relatesTo.element.Target.Identifier, subjectNhsNumber); var pointers = await _fhirSearch.Find <DocumentReference>(pointerRequest) as Bundle; if (pointers.Entry.Count != 1) { //Cant find related document return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target")); } //Custodian already validated against incoming ASID during ValidateCreate var custodianOdsCode = _fhirValidation.GetOrganizationReferenceId(document.Custodian); var oldDocument = pointers.Entry.First().Resource as DocumentReference; if (oldDocument.Custodian == null || string.IsNullOrEmpty(oldDocument.Custodian.Reference) || oldDocument.Custodian.Reference != $"{FhirConstants.SystemODS}{custodianOdsCode}") { //related document does not have same custodian return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target")); } if (oldDocument.Status != DocumentReferenceStatus.Current) { //Only allowed to transition to superseded from current return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.code")); } return(oldDocument); }
public void CreatePatient_Returns_Valid_Search() { var request = NrlsPointerHelper.CreatePatientSearch(FhirRequests.Valid_Create, "1445545101"); var expected = new List <Tuple <string, string> > { new Tuple <string, string>("identifier", $"{FhirConstants.SystemNhsNumber}|1445545101") }; Assert.Equal(expected, request.QueryParameters, Comparers.ModelComparer <IEnumerable <Tuple <string, string> > >()); Assert.Equal("Patient", request.StrResourceType); Assert.Equal("http://hl7.org/fhir/STU3/patient.html", request.ProfileUri); }
public void CreateMasterId_Returns_Valid_Search() { var masterIdentifier = new Identifier("validSystem", "validValue"); var request = NrlsPointerHelper.CreateMasterIdentifierSearch(FhirRequests.Valid_Create, masterIdentifier, "nhsNumber"); var expectedParams = new List <Tuple <string, string> > { new Tuple <string, string>("identifier", "validSystem|validValue"), new Tuple <string, string>("subject", $"{FhirConstants.SystemPDS}nhsNumber") }; Assert.Equal(expectedParams, request.QueryParameters, Comparers.ModelComparer <IEnumerable <Tuple <string, string> > >()); Assert.Contains("identifier", request.AllowedParameters); }
/// <summary> /// Search for Documents or Get one by _id /// </summary> /// <remarks> /// As the NRLS is implemented with just a search and not read, to read a document the _id parameter is supplied /// </remarks> public async Task <Bundle> Find(FhirRequest request) { ValidateResource(request.StrResourceType); request.ProfileUri = _resourceProfile; var id = request.IdParameter; //We are going to be strict hear for search and only proceed if we have valid parameters if (request.InvalidParameters.Count() > 0) { throw new HttpFhirException("Invalid parameters", OperationOutcomeFactory.CreateInvalidParameter($"Invalid parameter: {string.Join(", ", request.InvalidParameters)}"), HttpStatusCode.BadRequest); } //If we have an _id param it should be the only param so check for that here. if (request.HasIdParameter) { ObjectId mongoId; if (!ObjectId.TryParse(id, out mongoId)) { throw new HttpFhirException("Invalid _id parameter", OperationOutcomeFactory.CreateNotFound(id), HttpStatusCode.NotFound); } if (request.QueryParameters.Count() > 1) { throw new HttpFhirException("Invalid _id parameter combination", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: _id"), HttpStatusCode.BadRequest); } request.Id = id; request.IsIdQuery = true; var results = await _fhirSearch.GetAsBundle <DocumentReference>(request); var response = ParseRead(results, id) as Bundle; return(response); } //If we are here then it is a standard search query var patient = request.QueryParameters.FirstOrDefault(x => x.Item1 == "subject"); if (patient != null) { var invalidPatient = _fhirValidation.ValidatePatientParameter(patient.Item2); if (invalidPatient != null) { throw new HttpFhirException("Missing or Invalid patient parameter", invalidPatient, HttpStatusCode.BadRequest); } var nhsNumber = _fhirValidation.GetSubjectReferenceParameterId(patient.Item2); var patientRequest = NrlsPointerHelper.CreatePatientSearch(request, nhsNumber); var patients = await _fhirSearch.Find <Patient>(patientRequest); if (patients.Entry.Count == 0) { throw new HttpFhirException("Unknown patient", OperationOutcomeFactory.CreatePatientNotFound(nhsNumber), HttpStatusCode.NotFound); } } else { throw new HttpFhirException("Missing or Invalid patient parameter", OperationOutcomeFactory.CreateInvalidParameter("Missing parameter: subject"), HttpStatusCode.BadRequest); } var custodian = request.QueryParameters.FirstOrDefault(x => x.Item1 == "custodian"); var useCustodianIdentifierValidation = false; if (custodian == null) { //temporarily also support the incorrectly spec'd custodian.identifier parameter //custodian is a reference type and should not be used in this way custodian = request.QueryParameters.FirstOrDefault(x => x.Item1 == "custodian.identifier"); useCustodianIdentifierValidation = custodian != null; } if (custodian != null) { //temporarily also support the incorrectly spec'd custodian.identifier parameter var invalidCustodian = useCustodianIdentifierValidation ? _fhirValidation.ValidateCustodianIdentifierParameter(custodian.Item2) : _fhirValidation.ValidateCustodianParameter(custodian.Item2); if (invalidCustodian != null) { throw new HttpFhirException("Missing or Invalid custodian parameter", invalidCustodian, HttpStatusCode.BadRequest); } var custodianOrgCode = useCustodianIdentifierValidation? _fhirValidation.GetOrganizationParameterIdentifierId(custodian.Item2) : _fhirValidation.GetOrganizationParameterId(custodian.Item2); var custodianRequest = NrlsPointerHelper.CreateOrgSearch(request, custodianOrgCode); var custodians = await _fhirSearch.Find <Organization>(custodianRequest); if (custodians.Entry.Count == 0) { var invalidOrg = OperationOutcomeFactory.CreateOrganizationNotFound(custodianOrgCode); throw new HttpFhirException("Missing or Invalid custodian parameter", invalidOrg, HttpStatusCode.NotFound); } //This is now out of scope //var invalidOrgInteraction = ValidateOrganisationInteraction(request.RequestingAsid, custodianOrgCode, true); //if (invalidOrgInteraction != null) //{ // throw new HttpFhirException("Invalid Provider Request Exception", invalidOrgInteraction, HttpStatusCode.Unauthorized); //} } var type = request.QueryParameters.FirstOrDefault(x => x.Item1 == "type.coding"); if (type != null) { var invalidType = _fhirValidation.ValidTypeParameter(type.Item2); if (invalidType != null) { throw new HttpFhirException("Missing or Invalid type parameter", invalidType, HttpStatusCode.BadRequest); } } var summary = request.QueryParameters.FirstOrDefault(x => x.Item1 == "_summary"); if (summary != null) { var validSummaryParams = new string[] { "subject", "_format", "_summary" }; var invalidSummaryParams = request.QueryParameters.Where(x => !validSummaryParams.Contains(x.Item1)); OperationOutcome invalidSummary = null; if (invalidSummaryParams.Any()) { var invalidSummaryParamsList = string.Join(", ", invalidSummaryParams.Select(x => $"{x.Item1}={x.Item2}")); invalidSummary = OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter", $"Unsupported search parameter - {invalidSummaryParamsList}"); throw new HttpFhirException("Missing or Invalid type parameter", invalidSummary, HttpStatusCode.BadRequest); } invalidSummary = _fhirValidation.ValidSummaryParameter(summary.Item2); if (invalidSummary != null) { throw new HttpFhirException("Missing or Invalid type parameter", invalidSummary, HttpStatusCode.BadRequest); } request.IsSummary = true; } //Only allow current pointers var queryParameters = request.QueryParameters.ToList(); queryParameters.Add(new Tuple <string, string>("status", "current")); request.QueryParameters = queryParameters; request.AllowedParameters = request.AllowedParameters.Concat(new string[] { "status" }).ToArray(); return(await _fhirSearch.Find <DocumentReference>(request)); }
public async SystemTasks.Task <OperationOutcome> ValidateCreate(FhirRequest request) { ValidateResource(request.StrResourceType); request.ProfileUri = _resourceProfile; // NRLS Layers of validation before Fhir Search Call var document = request.Resource as DocumentReference; //NRL Pointer validation var validProfile = _fhirValidation.ValidPointer(document); if (!validProfile.Success) { throw new HttpFhirException("Invalid NRLS Pointer", validProfile, HttpStatusCode.BadRequest); } //Now we need to do some additional validation on ODS codes && Master Ids //We need to use an external source (in reality yes but we are just going to do an internal query to fake ods & pointer search) if (document.MasterIdentifier != null) { var nhsNumber = _fhirValidation.GetSubjectReferenceId(document.Subject); var masterIdentifierRequest = NrlsPointerHelper.CreateMasterIdentifierSearch(request, document.MasterIdentifier, nhsNumber); var miPointers = await _fhirSearch.GetByMasterId <DocumentReference>(masterIdentifierRequest); if (miPointers.Entry.Count > 0) { return(OperationOutcomeFactory.CreateDuplicateRequest(document.MasterIdentifier)); } } var custodianOrgCode = _fhirValidation.GetOrganizationReferenceId(document.Custodian); var invalidAsid = InvalidAsid(custodianOrgCode, request.RequestingAsid); if (invalidAsid != null) { return(invalidAsid); } var custodianRequest = NrlsPointerHelper.CreateOrgSearch(request, custodianOrgCode); var custodians = await _fhirSearch.Find <Organization>(custodianRequest); if (custodians.Entry.Count == 0) { return(OperationOutcomeFactory.CreateOrganizationNotFound(custodianOrgCode)); } var authorOrgCode = _fhirValidation.GetOrganizationReferenceId(document.Author?.FirstOrDefault()); var authorRequest = NrlsPointerHelper.CreateOrgSearch(request, authorOrgCode); var authors = await _fhirSearch.Find <Organization>(authorRequest); if (authors.Entry.Count == 0) { return(OperationOutcomeFactory.CreateOrganizationNotFound(authorOrgCode)); } //To better mimic NRL here we would create a new patient record for a patient that is not currently know (aka no clinical record) return(null); }
public async SystemTasks.Task <Resource> ValidateConditionalUpdate(FhirRequest request) { if (!request.Resource.ResourceType.Equals(ResourceType.DocumentReference)) { return(OperationOutcomeFactory.CreateInvalidResource("relatesTo")); } var document = request.Resource as DocumentReference; var relatesTo = _fhirValidation.GetValidRelatesTo(document.RelatesTo); if (document.RelatesTo.Count == 0) { //skip checks, request is just a standard create return(null); } else if (relatesTo.element == null) { return(OperationOutcomeFactory.CreateInvalidResource(relatesTo.issue)); } //Subject already validated during ValidateCreate //relatesTo Reference/Identifier already validated during ValidateCreate => validPointer var isRelatesToReference = !string.IsNullOrWhiteSpace(relatesTo.element.Target.Reference); FhirRequest pointerRequest = null; DocumentReference oldDocument = null; if (isRelatesToReference) { var pointerId = _fhirValidation.GetReferenceId(relatesTo.element.Target); pointerRequest = NrlsPointerHelper.CreateReferenceSearch(request, pointerId); oldDocument = await _fhirSearch.Get <DocumentReference>(pointerRequest); } else { var subjectNhsNumber = _fhirValidation.GetSubjectReferenceId(document.Subject); pointerRequest = NrlsPointerHelper.CreateMasterIdentifierSearch(request, relatesTo.element.Target.Identifier, subjectNhsNumber); var pointers = await _fhirSearch.Find <DocumentReference>(pointerRequest); //There should only ever be zero or one oldDocument = pointers.Entry.FirstOrDefault()?.Resource as DocumentReference; } if (oldDocument == null) { //Cant find related document return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target", "Referenced DocumentReference does not exist.")); } //related document does not have same patient if (string.IsNullOrEmpty(oldDocument.Subject.Reference) || oldDocument.Subject.Reference != document.Subject.Reference) { return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target", "Resolved DocumentReference is not associated with the same patient.")); } //Reference type checks if (isRelatesToReference) { //related document does not have masterIdentifier or masterIdentifier does not match new var docRelatesToIdentifier = document.RelatesTo.First().Target.Identifier; if (docRelatesToIdentifier != null) { var oldDocRelatesToIdentifier = oldDocument.MasterIdentifier; if (oldDocRelatesToIdentifier == null) { return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target", "Resolved DocumentReference does not have an MasterIdentifier.")); } if (string.IsNullOrWhiteSpace(docRelatesToIdentifier.System) || string.IsNullOrWhiteSpace(docRelatesToIdentifier.Value) || docRelatesToIdentifier.Value != oldDocRelatesToIdentifier.Value || docRelatesToIdentifier.System != oldDocRelatesToIdentifier.System) { return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target", "Resolved DocumentReference does not have a matching MasterIdentifier.")); } } } //Custodian already validated against incoming ASID during ValidateCreate var custodianOdsCode = _fhirValidation.GetOrganizationReferenceId(document.Custodian); if (oldDocument.Custodian == null || string.IsNullOrEmpty(oldDocument.Custodian.Reference) || oldDocument.Custodian.Reference != $"{FhirConstants.SystemODS}{custodianOdsCode}") { //related document does not have same custodian return(OperationOutcomeFactory.CreateInvalidResource("relatesTo.target", $"Resolved DocumentReference is not associated with custodian {custodianOdsCode}")); } if (oldDocument.Status != DocumentReferenceStatus.Current) { //Only allowed to transition to superseded from current return(OperationOutcomeFactory.CreateInactiveDocumentReference()); } return(oldDocument); }
public void CreateMasterId_Returns_Null() { var request = NrlsPointerHelper.CreateMasterIdentifierSearch(FhirRequests.Valid_Create, null, "nhsNumber"); Assert.Null(request); }
public void CreateOrg_Returns_Null() { var request = NrlsPointerHelper.CreateOrgSearch(FhirRequests.Valid_Create, null); Assert.Null(request); }
public void CreateReferenceSearch_Invalid_EmptyLogicalId() { var request = NrlsPointerHelper.CreateReferenceSearch(FhirRequests.Valid_Create, ""); Assert.Null(request); }