Exemple #1
0
        public async Task <IActionResult> Read(string id)
        {
            var request = FhirRequest.Create(id, ResourceType.Patient, null, Request, null);

            var result = await _pdsSearch.Get(request);

            if (result == null)
            {
                return(NotFound(OperationOutcomeFactory.CreateNotFound(id)));
            }

            return(Ok(result));
        }
        //[ProducesResponseType(typeof(DocumentReference), 200)]
        public async Task <IActionResult> Get([FromServices] INodeServices nodeServices, string documentId, string documentType)
        {
            //Not supporting 410 errors
            var regex = new Regex("^[A-Fa-f0-9-]{1,1024}$");

            var documentTypeTemplate = GetDocumentType(documentType);

            //TODO: check pointers cache
            if (documentTypeTemplate == null || string.IsNullOrWhiteSpace(documentId) || !regex.IsMatch(documentId))
            {
                throw new HttpFhirException("Not Found", OperationOutcomeFactory.CreateNotFound(documentId), HttpStatusCode.NotFound);
                //return NotFound(OperationOutcomeFactory.CreateNotFound(documentId));
            }


            //TODO: switch to other types
            var outputType = "application/pdf";

            var responseOutputType = GetOutputType();

            var template = GetTemplate(documentTypeTemplate);
            var model    = JsonConvert.SerializeObject(new { documentId = documentId });

            var data = await nodeServices.InvokeAsync <byte[]>("./Documents/Parsers/pdf", template, model);

            var result = data;


            if (!string.IsNullOrEmpty(responseOutputType) && responseOutputType.ToLowerInvariant().Contains("fhir"))
            {
                var binary = new Binary
                {
                    ContentType = outputType,
                    Content     = data
                };

                result = new FhirJsonSerializer().SerializeToBytes(binary);

                if (!responseOutputType.Contains("charset"))
                {
                    responseOutputType = $"{responseOutputType}; charset={Encoding.UTF8.WebName}";
                }
            }
            else
            {
                responseOutputType = outputType;
            }


            return(new FileContentResult(result, responseOutputType));
        }
Exemple #3
0
        protected Resource ParseRead <T>(T results, string id) where T : Resource
        {
            var notFound = results == null;

            if (!notFound && results.ResourceType == ResourceType.Bundle)
            {
                var bundle = results as Bundle;
                notFound = (!bundle.Total.HasValue || bundle.Total.Value != 1);
            }

            if (notFound)
            {
                return(OperationOutcomeFactory.CreateNotFound(id));
            }

            return(results);
        }
Exemple #4
0
        protected Resource ParseRead <T>(T results, string id) where T : Resource
        {
            var notFound = results == null;

            if (!notFound && results.ResourceType == ResourceType.Bundle)
            {
                var bundle = results as Bundle;
                notFound = (!bundle.Total.HasValue || bundle.Total.Value != 1);
            }

            if (notFound)
            {
                throw new HttpFhirException("Not Found", OperationOutcomeFactory.CreateNotFound(id), HttpStatusCode.NotFound);
            }

            return(results);
        }
        public async Task <IActionResult> Read(string odsCode)
        {
            var request = FhirRequest.Create(null, ResourceType.Organization, null, Request, null);

            request.QueryParameters = new List <Tuple <string, string> > {
                new Tuple <string, string>("identifier", $"{FhirConstants.SystemOrgCode}|{odsCode?.ToUpperInvariant()}")
            };

            var result = await _odsSearch.GetByQuery(request);

            if (result == null)
            {
                return(NotFound(OperationOutcomeFactory.CreateNotFound(odsCode)));
            }

            return(Ok(result));
        }
        public async Task <IActionResult> Read(string logicalId)
        {
            //TODO: Update to reflect new ID parameter
            var request = FhirRequest.Create(logicalId, ResourceType.DocumentReference, null, Request, RequestingAsid());

            var result = await _nrlsSearch.Get(request);

            if (result == null)
            {
                return(NotFound(OperationOutcomeFactory.CreateNotFound(logicalId)));
            }

            if ((result as DocumentReference).Status != DocumentReferenceStatus.Current)
            {
                return(BadRequest(OperationOutcomeFactory.CreateInactiveDocumentReference()));
            }

            return(Ok(result));
        }
        /// <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));
        }
        /// <summary>
        /// Delete a DocumentReference using the id in the path or the id value found in the request _id query parameter, or by Master Identifier
        /// </summary>
        /// <remarks>
        /// First we do a search to get the document, then we check the incoming ASID associated OrgCode against the custodian on the document.
        /// If valid we can delete.
        /// We use the FhirMaintain service and FhirSearch service to facilitate this
        /// </remarks>
        public async SystemTasks.Task <OperationOutcome> Delete(FhirRequest request)
        {
            ValidateResource(request.StrResourceType);

            request.ProfileUri = _resourceProfile;

            // NRLS Layers of validation before Fhir Delete Call
            //If we have id path segment we should have nothing else
            if (!string.IsNullOrEmpty(request.Id) && (!string.IsNullOrEmpty(request.IdParameter) || !string.IsNullOrEmpty(request.IdentifierParameter) || !string.IsNullOrEmpty(request.SubjectParameter)))
            {
                throw new HttpFhirException("Invalid query parameters for Delete by Logical Id", OperationOutcomeFactory.CreateInvalidParameter("Invalid query parameters for Delete by Logical Id"), HttpStatusCode.BadRequest);
            }

            //If we did not start from HTTP DELETE DocRef/[id] set id
            if (string.IsNullOrEmpty(request.Id))
            {
                request.Id = request.IdParameter;
            }

            var identifier = request.IdentifierParameter;
            var identifierValidationResult = _fhirValidation.ValidateIdentifierParameter("identifier", identifier);
            var subject = request.SubjectParameter;
            var subjectValidationResult = _fhirValidation.ValidatePatientParameter(subject);


            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult != null && subjectValidationResult != null)
            {
                throw new HttpFhirException("Missing or Invalid _id parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: _id"), HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult == null && subjectValidationResult != null)
            {
                throw new HttpFhirException("Missing or Invalid subject parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: subject"), HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult != null && subjectValidationResult == null)
            {
                throw new HttpFhirException("Missing or Invalid identifier parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: identifier"), HttpStatusCode.BadRequest);
            }


            Resource document;

            if (!string.IsNullOrEmpty(request.Id))
            {
                ObjectId mongoId;

                if (!ObjectId.TryParse(request.Id, out mongoId))
                {
                    throw new HttpFhirException("Invalid _id parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter", $"The Logical ID format does not apply to the given Logical ID - {request.Id}"), HttpStatusCode.BadRequest);
                }

                request.IsIdQuery = true;

                document = await _fhirSearch.GetAsBundle <DocumentReference>(request);
            }
            else
            {
                document = await _fhirSearch.GetByMasterId <DocumentReference>(request);
            }

            var documentResponse = ParseRead(document, request.Id);

            if (documentResponse.ResourceType == ResourceType.Bundle)
            {
                var result = documentResponse as Bundle;

                if (!result.Total.HasValue || result.Total.Value < 1 || result.Entry.FirstOrDefault() == null)
                {
                    return(OperationOutcomeFactory.CreateNotFound(request.Id));
                }

                var orgDocument = result.Entry.FirstOrDefault().Resource as DocumentReference;

                var orgCode = _fhirValidation.GetOrganizationReferenceId(orgDocument.Custodian);

                var invalidAsid = InvalidAsid(orgCode, request.RequestingAsid);

                if (invalidAsid != null)
                {
                    return(invalidAsid);
                }
            }
            else
            {
                return(documentResponse as OperationOutcome);
            }

            bool deleted;

            if (!string.IsNullOrEmpty(request.Id))
            {
                deleted = await _fhirMaintain.Delete <DocumentReference>(request);
            }
            else
            {
                //Add identifier on the fly as it is not a standard search parameter
                request.AllowedParameters = request.AllowedParameters.Concat(new[] { "identifier" }).ToArray();

                deleted = await _fhirMaintain.DeleteConditional <DocumentReference>(request);
            }

            if (!deleted)
            {
                return(OperationOutcomeFactory.CreateNotFound(request.Id));
            }

            return(OperationOutcomeFactory.CreateDelete(request.RequestUrl?.AbsoluteUri, request.AuditId));
        }
        /// <summary>
        /// Update a DocumentReference using the id in the path or by Master Identifier
        /// </summary>
        /// <remarks>
        /// First we do a search to get the document, then we check the incoming ASID associated OrgCode against the custodian on the document.
        /// If valid we can patch.
        /// We use the FhirMaintain service and FhirSearch service to facilitate this
        /// </remarks>
        public async SystemTasks.Task <OperationOutcome> Patch(FhirRequest request)
        {
            ValidateResource(request.StrResourceType);

            request.ProfileUri = _resourceProfile;

            // NRLS Layers of validation before Fhir Delete Call
            //If we have id path segment we should have nothing else
            if (!string.IsNullOrEmpty(request.Id) && (!string.IsNullOrEmpty(request.IdentifierParameter) || !string.IsNullOrEmpty(request.SubjectParameter)))
            {
                throw new HttpFhirException("Invalid query parameters for Update by Logical Id", OperationOutcomeFactory.CreateInvalidParameter("Invalid query parameters for Update by Logical Id"), HttpStatusCode.BadRequest);
            }

            var identifier = request.IdentifierParameter;
            var identifierValidationResult = _fhirValidation.ValidateIdentifierParameter("identifier", identifier);
            var subject = request.SubjectParameter;
            var subjectValidationResult = _fhirValidation.ValidatePatientParameter(subject);


            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult != null && subjectValidationResult != null)
            {
                throw new HttpFhirException("Missing or Invalid id", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: id"), HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult == null && subjectValidationResult != null)
            {
                throw new HttpFhirException("Missing or Invalid subject parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: subject"), HttpStatusCode.BadRequest);
            }

            if (string.IsNullOrEmpty(request.Id) && identifierValidationResult != null && subjectValidationResult == null)
            {
                throw new HttpFhirException("Missing or Invalid identifier parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter: identifier"), HttpStatusCode.BadRequest);
            }

            var parameters = request.Resource as Parameters;

            var validParameters = _fhirValidation.ValidateParameters(parameters);

            if (!validParameters.Success)
            {
                throw new HttpFhirException("Missing or Invalid parameter", validParameters, HttpStatusCode.BadRequest);
            }

            Resource documentBundle;

            if (!string.IsNullOrEmpty(request.Id))
            {
                ObjectId mongoId;

                if (!ObjectId.TryParse(request.Id, out mongoId))
                {
                    throw new HttpFhirException("Invalid id parameter", OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter", $"The Logical ID format does not apply to the given Logical ID - {request.Id}"), HttpStatusCode.BadRequest);
                }

                request.IsIdQuery = true;

                documentBundle = await _fhirSearch.GetAsBundle <DocumentReference>(request);
            }
            else
            {
                documentBundle = await _fhirSearch.GetByMasterId <DocumentReference>(request);
            }

            var    documentResponse = ParseRead(documentBundle, request.Id);
            string oldVersionId;

            if (documentResponse.ResourceType == ResourceType.Bundle)
            {
                var result = documentResponse as Bundle;

                if (!result.Total.HasValue || result.Total.Value < 1 || result.Entry.FirstOrDefault() == null)
                {
                    return(OperationOutcomeFactory.CreateNotFound(request.Id));
                }

                var orgDocument = result.Entry.FirstOrDefault().Resource as DocumentReference;
                oldVersionId = orgDocument.Meta?.VersionId;

                var orgCode = _fhirValidation.GetOrganizationReferenceId(orgDocument.Custodian);

                var invalidAsid = InvalidAsid(orgCode, request.RequestingAsid);

                if (invalidAsid != null)
                {
                    return(invalidAsid);
                }

                if (orgDocument.Status != DocumentReferenceStatus.Current)
                {
                    //Only allowed to transition to entered-in-error from current
                    return(OperationOutcomeFactory.CreateInactiveDocumentReference());
                }

                if (string.IsNullOrEmpty(request.Id))
                {
                    request.Id = orgDocument.Id;
                }
            }
            else
            {
                return(documentResponse as OperationOutcome);
            }


            UpdateDefinition <BsonDocument> updates = null;

            BuildEnteredInError(oldVersionId, out updates);

            var patched = await _fhirMaintain.Update <DocumentReference>(request, updates);

            if (!patched)
            {
                return(OperationOutcomeFactory.CreateNotFound(request.Id));
            }

            return(OperationOutcomeFactory.CreateUpdated(request.RequestUrl?.AbsoluteUri, request.AuditId));
        }