public OperationOutcome ValidateContent(List <DocumentReference.ContentComponent> contents)
        {
            //profile checker checks
            // - has at least one content
            // - has valid format
            // - has valid contentStability extension
            // - has 1 attachment
            //    - has valid creation date
            //    -
            foreach (var content in contents)
            {
                //attachment.contentType
                //TODO validate contenttype format
                var contentType = content.Attachment.ContentType;
                if (string.IsNullOrEmpty(contentType))
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("contenttype"));
                }

                //attachment.url
                var url = content.Attachment.Url;
                if (string.IsNullOrEmpty(url) || !FhirUri.IsValidValue(url))
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("url"));
                }
            }

            return(null);
        }
        public async SystemTasks.Task <Resource> SupersedeWithoutValidation(FhirRequest request, string oldDocumentId, string oldVersionId)
        {
            UpdateDefinition <BsonDocument> updates = null;
            FhirRequest updateRequest = null;

            SetMetaValues(request);
            BuildSupersede(oldDocumentId, oldVersionId, out updates, out updateRequest);

            Resource created;
            bool     updated;

            try
            {
                (created, updated) = await _fhirMaintain.CreateWithUpdate <DocumentReference>(request, updateRequest, updates);
            }
            catch
            {
                throw new HttpFhirException("Error Updating DocumentReference", OperationOutcomeFactory.CreateInternalError($"There has been an internal error when attempting to persist the DocumentReference. Please contact the national helpdesk quoting - {Guid.NewGuid()}"));
            }

            var response = created;

            if (response == null)
            {
                response = OperationOutcomeFactory.CreateInvalidResource("Unknown");
            }

            if (!updated)
            {
                response = OperationOutcomeFactory.CreateInvalidResource("relatesTo");
            }

            return(response);
        }
        private OperationOutcome InvalidAsid(string orgCode, string asid)
        {
            var cache = _sdsService.GetFor(asid);

            if (cache != null && !string.IsNullOrEmpty(orgCode) && !string.IsNullOrEmpty(cache.OdsCode) && cache.OdsCode == orgCode)
            {
                return(null);
            }

            return(OperationOutcomeFactory.CreateInvalidResource(FhirConstants.HeaderFromAsid, "The Custodian ODS code is not affiliated with the sender ASID."));
        }
Exemple #4
0
        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 async SystemTasks.Task <Resource> CreateWithoutValidation(FhirRequest request)
        {
            SetMetaValues(request);

            var response = await _fhirMaintain.Create <DocumentReference>(request);

            if (response == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("Unknown"));
            }

            return(response);
        }
Exemple #6
0
        public OperationOutcome ValidatePatientReference(ResourceReference reference)
        {
            if (!_validationHelper.ValidReference(reference, FhirConstants.SystemPDS))
            {
                return(OperationOutcomeFactory.CreateInvalidResource("subject"));
            }

            var nhsNumber = reference.Reference.Replace(FhirConstants.SystemPDS, "");

            if (!_validationHelper.ValidNhsNumber(nhsNumber))
            {
                return(OperationOutcomeFactory.CreateInvalidNhsNumberRes(nhsNumber));
            }

            return(null);
        }
Exemple #7
0
        private OperationOutcome InvalidAsid(string orgCode, string asid, bool isCreate)
        {
            var map = _cache.Get <ClientAsidMap>(ClientAsidMap.Key);

            if (!string.IsNullOrEmpty(asid) && map != null && map.ClientAsids != null)
            {
                var asidMap = map.ClientAsids.FirstOrDefault(x => x.Key == asid);

                if (asidMap.Value != null && !string.IsNullOrEmpty(orgCode) && !string.IsNullOrEmpty(asidMap.Value.OrgCode) && asidMap.Value.OrgCode == orgCode)
                {
                    return(null);
                }
            }

            return(OperationOutcomeFactory.CreateInvalidResource(FhirConstants.HeaderFromAsid, "The Custodian ODS code is not affiliated with the sender ASID."));
        }
Exemple #8
0
        public OperationOutcome ValidateOrganisationReference(ResourceReference reference, string type)
        {
            if (!_validationHelper.ValidReference(reference, FhirConstants.SystemODS))
            {
                return(OperationOutcomeFactory.CreateInvalidParameter("Invalid parameter", $"The given resource URL does not conform to the expected format - {FhirConstants.SystemODS}/[ODS Code]"));
            }

            var orgCode = GetOrganizationReferenceId(reference);

            if (string.IsNullOrWhiteSpace(orgCode))
            {
                return(OperationOutcomeFactory.CreateInvalidResource(type));
            }


            return(null);
        }
Exemple #9
0
        public OperationOutcome ValidateContent(List <DocumentReference.ContentComponent> contents)
        {
            if (contents == null || contents.Count == 0)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("content"));
            }

            foreach (var content in contents)
            {
                //attachment
                if (content.Attachment == null)
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("attachment"));
                }

                //attachment.contentType
                //TODO validate content type format
                var contentType = content.Attachment.ContentType;
                if (string.IsNullOrEmpty(contentType))
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("contenttype"));
                }

                //attachment.url
                var url = content.Attachment.Url;
                if (string.IsNullOrEmpty(url) || !FhirUri.IsValidValue(url))
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("url"));
                }

                //attachment.creation can be empty
                var      creation = content.Attachment.Creation;
                DateTime validCreation;
                if (!string.IsNullOrEmpty(creation) && (!FhirDateTime.IsValidValue(creation) || !DateTime.TryParse(creation, out validCreation)))
                {
                    return(OperationOutcomeFactory.CreateInvalidResource("creation", $"The attachment creation date value is not a valid dateTime type: {creation}."));
                }
            }

            return(null);
        }
        private UpdateDefinition <BsonDocument> UpdateResource(string oldVersion, string status, string resourceTarget)
        {
            int version      = 0;
            int newVersion   = 1;
            var validVersion = int.TryParse(oldVersion, out version);

            if (validVersion)
            {
                newVersion = version + 1;
            }

            if (!string.IsNullOrEmpty(oldVersion) && !validVersion)
            {
                throw new HttpFhirException("Bad update values", OperationOutcomeFactory.CreateInvalidResource(resourceTarget), HttpStatusCode.BadRequest);
            }

            return(new UpdateDefinitionBuilder <BsonDocument>()
                   .Set("status", status)
                   .Set("meta.lastUpdated", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"))
                   .Set("meta.versionId", $"{newVersion}"));
        }
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                var error = context.ModelState.First();

                var message = error.Value.Errors.FirstOrDefault()?.ErrorMessage;

                OperationOutcome outcome = null;

                if (error.Key == "InputFormatter")
                {
                    outcome = OperationOutcomeFactory.CreateInvalidRequest();
                }
                else
                {
                    outcome = OperationOutcomeFactory.CreateInvalidResource(error.Key, message);
                }

                throw new HttpFhirException(message, outcome, HttpStatusCode.BadRequest);
            }
        }
Exemple #12
0
        public void BuildSupersede(string oldDocumentId, string oldVersion, out UpdateDefinition <BsonDocument> updates, out FhirRequest updateRequest)
        {
            int version      = 0;
            int newVersion   = 1;
            var validVersion = int.TryParse(oldVersion, out version);

            if (validVersion)
            {
                newVersion = version + 1;
            }

            if (!string.IsNullOrEmpty(oldVersion) && !validVersion)
            {
                throw new HttpFhirException("Bad update values", OperationOutcomeFactory.CreateInvalidResource("relatesTo"), HttpStatusCode.BadRequest);
            }

            updates = new UpdateDefinitionBuilder <BsonDocument>()
                      .Set("status", DocumentReferenceStatus.Superseded.ToString().ToLowerInvariant())
                      .Set("meta.lastUpdated", DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"))
                      .Set("meta.versionId", $"{newVersion}");

            updateRequest = FhirRequest.Create(oldDocumentId, ResourceType.DocumentReference);
        }
        public OperationOutcome ValidateParameters(Parameters parameters)
        {
            //TODO: make more generic

            if (parameters.Parameter == null || parameters.Parameter.Count == 0)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("parameter"));
            }

            var parameter = parameters.Parameter.First();

            if (parameter.Name != "operation")
            {
                return(OperationOutcomeFactory.CreateInvalidResource("parameter.name"));
            }

            var typePart  = parameter.Part?.FirstOrDefault(x => x.Name == "type");
            var pathPart  = parameter.Part?.FirstOrDefault(x => x.Name == "path");
            var valuePart = parameter.Part?.FirstOrDefault(x => x.Name == "value");

            if (typePart == null || (typePart.Value as Code) == null || (typePart.Value as Code).Value != "replace")
            {
                return(OperationOutcomeFactory.CreateInvalidResource("parameter.part.type"));
            }

            if (pathPart == null || (pathPart.Value as FhirString) == null || (pathPart.Value as FhirString).Value != "DocumentReference.status")
            {
                return(OperationOutcomeFactory.CreateInvalidResource("parameter.part.path"));
            }

            if (valuePart == null || (valuePart.Value as FhirString) == null || (valuePart.Value as FhirString).Value != "entered-in-error")
            {
                return(OperationOutcomeFactory.CreateInvalidResource("parameter.part.value"));
            }

            return(OperationOutcomeFactory.CreateOk());
        }
Exemple #14
0
        public OperationOutcome ValidPointer(DocumentReference pointer)
        {
            //master identifier
            if (pointer.MasterIdentifier != null)
            {
                var masterIdentifierCheck = _validationHelper.ValidIdentifier(pointer.MasterIdentifier, "masterIdentifier");

                if (!masterIdentifierCheck.valid)
                {
                    return(OperationOutcomeFactory.CreateInvalidResource(masterIdentifierCheck.issue, "If the masterIdentifier is supplied then the value and system properties are mandatory"));
                }
            }

            //status
            if (!GetValidStatus(pointer.Status).HasValue)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("status", "The status of a new DocumentReference can only be \"current\""));
            }

            //type
            if (pointer.Type == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }
            else if (!_validationHelper.ValidCodableConcept(pointer.Type, FhirConstants.SystemPointerType, true, true, true, true, FhirConstants.VsRecordType))
            {
                return(OperationOutcomeFactory.CreateInvalidResource("type"));
            }

            //subject
            if (pointer.Subject != null)
            {
                var validNhsNumber = ValidatePatientReference(pointer.Subject);

                if (validNhsNumber != null)
                {
                    return(validNhsNumber);
                }
            }
            else
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }

            //validate orgcode is real done outside of here
            //author
            if (pointer.Author != null && pointer.Author.Count == 1)
            {
                var validAuthor = ValidateOrganisationReference(pointer.Author.First(), "author");

                if (validAuthor != null)
                {
                    return(validAuthor);
                }
            }
            else
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }


            //validate orgcode for match against fromASID and is real done outside of here
            //custodian
            if (pointer.Custodian != null)
            {
                var validCustodian = ValidateOrganisationReference(pointer.Custodian, "custodian");

                if (validCustodian != null)
                {
                    return(validCustodian);
                }
            }
            else
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }

            //indexed
            DateTime validIndexed;

            if (pointer.Indexed == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }
            else if (!pointer.Indexed.HasValue || !FhirDateTime.IsValidValue(pointer.Indexed.Value.ToString(DateTimeFormat)) || !DateTime.TryParse(pointer.Indexed.Value.ToString(DateTimeFormat), out validIndexed))
            {
                return(OperationOutcomeFactory.CreateInvalidResource("indexed"));
            }

            //relatesTo
            //Only require basic checks here
            //Additional checks are carried out in NrlsMaintain.ValidateConditionalUpdate
            var relatesTo = GetValidRelatesTo(pointer.RelatesTo);

            if (pointer.RelatesTo != null && pointer.RelatesTo.Count > 0 && relatesTo.element == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource(relatesTo.issue));
            }

            //attachment
            if (pointer.Content != null)
            {
                var validContent = ValidateContent(pointer.Content);

                if (validContent != null)
                {
                    return(validContent);
                }
            }
            else
            {
                return(OperationOutcomeFactory.CreateInvalidResource(null));
            }

            return(OperationOutcomeFactory.CreateOk());
        }
        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 OperationOutcome ValidPointer(DocumentReference pointer)
        {
            var profile = (pointer.Meta?.Profile?.Contains(FhirConstants.SystemNrlsPrefProfile)) == true ? FhirConstants.SystemNrlsPrefProfile :
                          FhirConstants.SystemNrlsProfile;

            //Base NRL Pointer Validation
            var profileCheck = _validationHelper.ValidateResource(pointer, profile);

            if (!profileCheck.Success)
            {
                var error = profileCheck.Issue.FirstOrDefault(x => x.Severity.HasValue && new OperationOutcome.IssueSeverity[] { OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueSeverity.Fatal }.Contains(x.Severity.Value));

                //NOTE: structure of property and diagnostics will not match NRL output
                return(OperationOutcomeFactory.CreateInvalidResource(error.Location.First(), error.Details.Text));
            }

            //Below checks cover what profile checker can't check


            //profile check
            var profiles = pointer.Meta?.Profile;

            if (profiles == null || profiles.Count() != 1 || profiles.FirstOrDefault(x => new string[] { FhirConstants.SystemNrlsProfile, FhirConstants.SystemNrlsPrefProfile }.Contains(x)) == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("meta.Profile", $"There must be a single profile of the value {FhirConstants.SystemNrlsProfile} or {FhirConstants.SystemNrlsPrefProfile}"));
            }

            //master identifier
            if (pointer.MasterIdentifier != null)
            {
                var masterIdentifierCheck = _validationHelper.ValidIdentifier(pointer.MasterIdentifier, "masterIdentifier");

                if (!masterIdentifierCheck.valid)
                {
                    return(OperationOutcomeFactory.CreateInvalidResource(masterIdentifierCheck.issue, "If the masterIdentifier is supplied then the value and system properties are mandatory"));
                }
            }

            //status
            if (!GetValidStatus(pointer.Status).HasValue)
            {
                return(OperationOutcomeFactory.CreateInvalidResource("status", "The status of a new DocumentReference can only be \"current\""));
            }

            //subject
            //Just NHS Number Validation
            var validNhsNumber = ValidatePatientReference(pointer.Subject);

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

            //author
            //validate orgcode is done outside of here
            var validAuthor = ValidateOrganisationReference(pointer.Author.First(), "author");

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


            //custodian
            //validate orgcode for match against fromASID is done outside of here
            var validCustodian = ValidateOrganisationReference(pointer.Custodian, "custodian");

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

            //relatesTo
            //Only require basic checks here
            //Additional checks are carried out in NrlsMaintain.ValidateConditionalUpdate
            var relatesTo = GetValidRelatesTo(pointer.RelatesTo);

            if (pointer.RelatesTo.Count > 0 && relatesTo.element == null)
            {
                return(OperationOutcomeFactory.CreateInvalidResource(relatesTo.issue));
            }

            //Content
            var validContent = ValidateContent(pointer.Content);

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

            //Context
            //Checked in profile checker
            var period = pointer.Context.Period;

            if (period != null && string.IsNullOrEmpty(period.Start))
            {
                return(OperationOutcomeFactory.CreateInvalidResource("context.period", "When included the period element must have a valid start dateTime."));
            }
            //WARNING: practice setting valueset is stored locally

            return(OperationOutcomeFactory.CreateOk());
        }