public override void OnException(HttpActionExecutedContext context) { HttpResponseMessage response = null; if (context.Exception is PyroException PyroException) { if (PyroException.OperationOutcome != null) { IFhirResourceNarrative.CreateNarrative(PyroException.OperationOutcome); response = context.Request.CreateResponse <Resource>(PyroException.HttpStatusCode, PyroException.OperationOutcome); } else { OperationOutcome OpOutCome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.Unknown, PyroException.Message); IFhirResourceNarrative.CreateNarrative(OpOutCome); response = context.Request.CreateResponse(PyroException.HttpStatusCode, OpOutCome); } } else if (context.Exception is SqlException oSqlException) { PyroException PyroSqlException = SqlExceptionSupport.GenerateDtoPyroException(oSqlException, System.Diagnostics.Debugger.IsAttached); IFhirResourceNarrative.CreateNarrative(PyroSqlException.OperationOutcome); response = context.Request.CreateResponse <Resource>(PyroSqlException.HttpStatusCode, PyroSqlException.OperationOutcome); } else if (context.Exception is Exception) { OperationOutcome OpOutCome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.Unknown, context.Exception.ToString()); IFhirResourceNarrative.CreateNarrative(OpOutCome); response = context.Request.CreateResponse <Resource>(HttpStatusCode.InternalServerError, OpOutCome); } throw new HttpResponseException(response); }
private ITriggerOutcome ProcessUpdateOrDelete(ITriggerInput TriggerInput) { //Get any Compartment with the same FhirId DtoServiceSearchParameterLight TagSearchParameter = GetTagSearchParameters(TriggerInput.ResourceType.GetLiteral()); if (TriggerInput.DbTokenIndexList.Any(x => x.ServiceSearchParameterId == TagSearchParameter.Id && x.Code == _ProtectedCoding.Code && x.System == _ProtectedCoding.System)) { //The Resource is protected string Message = "Error Message Not Set"; if (TriggerInput.CrudOperationType == RestEnum.CrudOperationType.Update) { Message = $"The {TriggerInput.ResourceType.GetLiteral()} resource instance with Id: {TriggerInput.InboundResourceId} cannot be updated because it is a {_ProtectedCoding.Display}."; } else if (TriggerInput.CrudOperationType == RestEnum.CrudOperationType.Delete) { Message = $"The {TriggerInput.ResourceType.GetLiteral()} resource instance with Id: {TriggerInput.InboundResourceId} cannot be deleted because it is a {_ProtectedCoding.Display}."; } var ReturnOperationOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.BusinessRule, Message); var TriggerOutcome = new TriggerOutcome(); TriggerOutcome.Report = true; TriggerOutcome.HttpStatusCode = System.Net.HttpStatusCode.Conflict; TriggerOutcome.Resource = ReturnOperationOutcome; return(TriggerOutcome); } else { //The resource is not Protected return(new TriggerOutcome() { Report = false }); } }
private ITriggerOutcome ProcessCreateUpdateOrDelete(ITriggerInput triggerInput) { if (IGlobalProperties.ServerReadOnlyMode) { //The Resource is protected string Message = $"The FHIR Server is currently in a read only mode. "; if (!string.IsNullOrWhiteSpace(IGlobalProperties.ServerReadOnlyModeMessage)) { Message += $"Administrator's Message: {IGlobalProperties.ServerReadOnlyModeMessage}"; } var ReturnOperationOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Information, OperationOutcome.IssueType.Informational, Message); var TriggerOutcome = new TriggerOutcome(); TriggerOutcome.Report = true; TriggerOutcome.HttpStatusCode = System.Net.HttpStatusCode.ServiceUnavailable; TriggerOutcome.Resource = ReturnOperationOutcome; return(TriggerOutcome); } else { //The resource is not Protected return(new TriggerOutcome() { Report = false }); } }
public PyroSearchParameters GetSearchParameters(string Compartment, string CompartmentId, string ResourceName) { //I need to check that Compartment and ResourceName are actual FHIR Resource Types, the two lines //below do that and throw Pyro Exception if they are not. FHIRAllTypes CompartmentType = ResourceNameResolutionSupport.GetResourceFhirAllType(Compartment); FHIRAllTypes ResourceNameType = ResourceNameResolutionSupport.GetResourceFhirAllType(ResourceName); //Now to contruct the Container search parameters, these are cached from the database Conatiner Resource DtoServiceCompartmentResourceCached ServiceCompartmentResource = IServiceCompartmentCache.GetServiceCompartmentResourceForCompartmentCodeAndResource(Compartment, ResourceName); string ConatinerSerachString = string.Empty; if (ServiceCompartmentResource != null) { var CompartmentParamQuery = new List <string>(); foreach (var CompartmentSearchParameter in ServiceCompartmentResource.ParamList) { if (CompartmentSearchParameter.Param == "*") { // if the param="*" then all instances of this Resource Type are in the container and there are no // actualy parameters that it needs to be restricted by. So the ConatinerSerachString remains as empty string. break; } else { CompartmentParamQuery.Add($"{CompartmentSearchParameter.Param}:{Compartment}={CompartmentId}"); } } ConatinerSerachString = String.Join("&", CompartmentParamQuery.ToArray()); } else { DtoServiceCompartmentCached ServiceCompartment = IServiceCompartmentCache.GetServiceCompartmentForCompartmentCode(Compartment); if (ServiceCompartment == null) { string Message = $"No active {Compartment} Compartment exist in this server. Perhaps you could create one using a {FHIRAllTypes.CompartmentDefinition.GetLiteral()} resource and the resource instance ${Pyro.Common.Enum.FhirOperationEnum.OperationType.xSetCompartmentActive} Operation. " + $"For example: '[base]/{FHIRAllTypes.CompartmentDefinition.GetLiteral()}/[id]/${Pyro.Common.Enum.FhirOperationEnum.OperationType.xSetCompartmentActive}' "; var OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.NotSupported, Message); throw new PyroException(System.Net.HttpStatusCode.BadRequest, OpOutcome, Message); } else { string Message = $"The {Compartment} Compartment defined by the {FHIRAllTypes.CompartmentDefinition.GetLiteral()} with the resource id of '{ServiceCompartment.CompartmentDefinitionResourceId}' does not allow access to any {ResourceName} resource type instances."; var OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.NotSupported, Message); throw new PyroException(System.Net.HttpStatusCode.BadRequest, OpOutcome, Message); } } ISearchParameterGeneric ContainerSearchParameterGeneric = ISearchParameterGenericFactory.CreateDtoSearchParameterGeneric().Parse(ConatinerSerachString); ISearchParameterService SearchService = ISearchParameterServiceFactory.CreateSearchParameterService(); ISearchParametersServiceOutcome ContainerSearchParametersServiceOutcome = SearchService.ProcessSearchParameters(ContainerSearchParameterGeneric, SearchParameterService.SearchParameterServiceType.Resource, ResourceNameType, null); return(ContainerSearchParametersServiceOutcome.SearchParameters); }
private void IdentifieBatchEntityToClient(OperationOutcome op, string FullURL, string OperationType, int EntityIndexInType) { EntityIndexInType = EntityIndexInType + 1; string Message = string.Empty; if (String.IsNullOrWhiteSpace(FullURL)) { Message = $"The Issue/s were found in the {EntityIndexInType.Ordinal()} {OperationType} bundel entry"; } else { Message = $"The Issue/s were found in the bundel entry identified by the FullURL: {FullURL}"; } OperationOutcome NewOp = FhirOperationOutcomeSupport.Append(OperationOutcome.IssueSeverity.Information, OperationOutcome.IssueType.Informational, Message, op); }
private ITriggerOutcome ProcessUpdateOrDelete(RestEnum.CrudOperationType crudOperationType, ResourceTriggerService.TriggerRaisedType triggerRaised, string resourceId, FHIRAllTypes resourceType, Resource resource) { //Get any Compartment with the same FhirId var ServiceCompartment = IServiceCompartmentRepository.GetServiceCompartmentByFhirId(resourceId); if (ServiceCompartment != null) { string Message = "Error Message Not Set"; if (crudOperationType == RestEnum.CrudOperationType.Update) { //If so do not allow the update Message = $"The {FHIRAllTypes.CompartmentDefinition.GetLiteral()} resource cannot be updated because it is an active Compartment in the server. " + $"You must first set this compartment to Inactive before you can updated this resource. " + $"To do this you will need to call the server Operation ${FhirOperationEnum.OperationType.xSetCompartmentInActive.GetPyroLiteral()} on this resource instance. " + $"For example '[base]/{FHIRAllTypes.CompartmentDefinition.GetLiteral()}/{resourceId}/${FhirOperationEnum.OperationType.xSetCompartmentInActive.GetPyroLiteral()}' " + $"Once Inactive you will then be able to update this resource in the server. " + $"Caution should be taken as active Compartments affect how users interact with the server and the resources they have access to. " + $"To re-activate the Compartment after updating you must call the Operation ${FhirOperationEnum.OperationType.xSetCompartmentActive.GetPyroLiteral()}. " + $"For example '[base]/{FHIRAllTypes.CompartmentDefinition.GetLiteral()}/{resourceId}/${FhirOperationEnum.OperationType.xSetCompartmentActive.GetPyroLiteral()}' "; } else if (crudOperationType == RestEnum.CrudOperationType.Delete) { Message = $"The {FHIRAllTypes.CompartmentDefinition.GetLiteral()} resource cannot be deleted because it is an active Compartment in the server. " + $"You must first set this compartment to Inactive before you can delete this resource. " + $"To do this you will need to call the server Operation ${FhirOperationEnum.OperationType.xSetCompartmentInActive.GetPyroLiteral()} on this resource instance. " + $"For example '[base]/{FHIRAllTypes.CompartmentDefinition.GetLiteral()}/{resourceId}/${FhirOperationEnum.OperationType.xSetCompartmentInActive.GetPyroLiteral()}' " + $"Once Inactive you will then be able to delete this resource from the server. " + $"Caution should be taken as active Compartments affect how users interact with the server and the resources they have access to."; } var ReturnOperationOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.BusinessRule, Message); var TriggerOutcome = new TriggerOutcome(); TriggerOutcome.HttpStatusCode = System.Net.HttpStatusCode.Conflict; TriggerOutcome.TriggerOutcomeResult = TriggerOutcome.TriggerOutcomeType.Report; TriggerOutcome.Resource = ReturnOperationOutcome; return(TriggerOutcome); } else { //the Compartmentdefinition is not active so can be Updated or deleted, if updating then ensure //the active tags are not present and remove if they are RemoveActiveCompartmentTag(resource); } return(new TriggerOutcome() { TriggerOutcomeResult = TriggerOutcome.TriggerOutcomeType.Contiune }); }
private OperationOutcome GenerateUnsupportedParameterOperationOutcome() { if (this.SearchParameters == null || this.SearchParameters.UnspportedSearchParameterList == null || this.SearchParameters.UnspportedSearchParameterList.Count == 0) { string ExceptionMessage = "Server Internal Error: Attempt to generate unsupported search parameter OperationOutcome when there were no unsupported search parameters to process."; throw new Pyro.Common.Exceptions.PyroException(HttpStatusCode.Forbidden, FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.Exception, ExceptionMessage), ExceptionMessage); } var OpOut = new OperationOutcome(); foreach (UnspportedSearchParameter Parameter in SearchParameters.UnspportedSearchParameterList) { string Message = $"Unable to process a search parameter: {Parameter.ReasonMessage}"; FhirOperationOutcomeSupport.Append(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Processing, Message, OpOut); } return(OpOut); }
private HttpResponseMessage ResolveReturnAcceptTypeJsonOrXml(string _FormatMimeType, HttpRequestMessage Request, Resource Resource) { HttpResponseMessage Response; //If no _format=application/fhir%2Bjson parameter if (_FormatMimeType == null) { //below returns Null if ok or an Operationoutcome if not ok OperationOutcome FailedAcceptHeaderOperationOutcome = null; FailedAcceptHeaderOperationOutcome = CheckAcceptHeaderValid(Request); if (FailedAcceptHeaderOperationOutcome != null) { IFhirResourceNarrative.CreateNarrative(FailedAcceptHeaderOperationOutcome); _HttpStatusCode = HttpStatusCode.UnsupportedMediaType; Response = Request.CreateResponse(_HttpStatusCode, FailedAcceptHeaderOperationOutcome); } else { //Then send the Accept type as specified by the Accept Header, or the default of JSON if no header at all Response = Request.CreateResponse(_HttpStatusCode, Resource); } } else { //Parse the _format=application/fhir%2Bjson parameter and switch formater if valid. _FhirMediaTypeFormatter = GetFhirMediaFormatter(_FormatMimeType); if (_FhirMediaTypeFormatter != null) { Response = new HttpResponseMessage(_HttpStatusCode) { Content = new ObjectContent(typeof(Resource), Resource, _FhirMediaTypeFormatter) }; } else { string Message = $"The _format search parameter value was {_FormatMimeType} which is not a supported media type. The simplest supported meda types to use are _format=xml or _format=json"; var FaledFormatParameterOperationOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.NotSupported, Message); IFhirResourceNarrative.CreateNarrative(FaledFormatParameterOperationOutcome); _HttpStatusCode = HttpStatusCode.UnsupportedMediaType; Response = Request.CreateResponse(_HttpStatusCode, FaledFormatParameterOperationOutcome); } } return(Response); }
private static OperationOutcome CheckAcceptHeaderValid(HttpRequestMessage Request) { //first test the Accept header is valid if (Request?.Headers?.Accept?.Count > 0) { bool IsOk = false; foreach (var AcceptHeaderValue in Request.Headers.Accept) { if (!string.IsNullOrWhiteSpace(Common.Tools.HttpHeaderSupport.GetFhirMediaTypeString(AcceptHeaderValue.MediaType))) { IsOk = true; break; } } if (!IsOk) { string Message = $"None of the Http Accept header values are suported by the server. The primary values that are supported are {Hl7.Fhir.Rest.ContentType.JSON_CONTENT_HEADER} and {Hl7.Fhir.Rest.ContentType.XML_CONTENT_HEADER}"; return(FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.NotSupported, Message)); } } return(null); }
public HttpResponseMessage GetHttpResponseMessage(IResourceServiceOutcome ResourceServiceOutcome, HttpRequestMessage Request, Hl7.Fhir.Rest.SummaryType?SummaryType) { _HttpStatusCode = ResourceServiceOutcome.HttpStatusCode; Resource Resource = ResourceServiceOutcome.ResourceResult; HttpResponseMessage Response = Request.CreateResponse(_HttpStatusCode); if (Resource != null) { //If the Resource is an OperationOutcome then we auto generate its narrative here if (Resource.ResourceType == ResourceType.OperationOutcome) { IFhirResourceNarrative.CreateNarrative(Resource as OperationOutcome); } //Set the media formatter as per search parameter _format Response = ResolveReturnAcceptTypeJsonOrXml(ResourceServiceOutcome.FormatMimeType, Request, Resource); //Annotate the Resource with the _summary, will get the annotation in MediaTypeFormatter XML or JSON AnnotateResourceWithSummaryType(SummaryType, Resource); } switch (_HttpStatusCode) { case HttpStatusCode.OK: { switch (ResourceServiceOutcome.OperationType) { case RestEnum.CrudOperationType.None: break; case RestEnum.CrudOperationType.Create: { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); //If we have a conditional Create where the Resource is found then we return OK but no Resource so no Content if (Response.Content != null) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } } return(Response); } case RestEnum.CrudOperationType.Read: { //LastModified Header & ETagVersion &Location Header if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); if (Response.Content != null && ResourceServiceOutcome.IsDeleted.HasValue && !ResourceServiceOutcome.IsDeleted.Value) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } return(Response); } case RestEnum.CrudOperationType.Update: { //Location Header if (Response.Content != null) { Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } case RestEnum.CrudOperationType.Delete: { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { if (!string.IsNullOrWhiteSpace(ResourceServiceOutcome.ResourceVersionNumber)) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); } Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } default: { var oIssueComponent = new OperationOutcome.IssueComponent { Severity = OperationOutcome.IssueSeverity.Fatal, Code = OperationOutcome.IssueType.Exception, Diagnostics = "Internal Error. FhirRestResponse contains no FHIR Resource or Id." }; var oOperationOutcome = new OperationOutcome { Issue = new List <OperationOutcome.IssueComponent>() { oIssueComponent } }; throw new PyroException(HttpStatusCode.InternalServerError, oOperationOutcome, "Internal Error. FhirRestResponse contains no FHIR Resource or Id."); } } if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Read) { //LastModified Header & ETagVersion &Location Header if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); if (Response.Content != null && ResourceServiceOutcome.IsDeleted.HasValue && !ResourceServiceOutcome.IsDeleted.Value) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Update) { //Location Header if (Response.Content != null) { Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Delete && ResourceServiceOutcome.ResourceVersionNumber != null) { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Create) { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); //If we have a conditional Create where the Resource is found then we return OK but no Resource so no Content if (Response.Content != null) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } } return(Response); } else { var oIssueComponent = new OperationOutcome.IssueComponent { Severity = OperationOutcome.IssueSeverity.Fatal, Code = OperationOutcome.IssueType.Exception, Diagnostics = "Internal Error. FhirRestResponse contains no FHIR Resource or Id." }; var oOperationOutcome = new OperationOutcome { Issue = new List <OperationOutcome.IssueComponent>() { oIssueComponent } }; throw new PyroException(HttpStatusCode.InternalServerError, oOperationOutcome, "Internal Error. FhirRestResponse contains no FHIR Resource or Id."); } } case HttpStatusCode.Created: { //Location Header if (Response.Content != null) { Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri, ResourceServiceOutcome.FhirResourceId); } //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } case HttpStatusCode.Gone: { //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); } return(Response); } case HttpStatusCode.NoContent: { //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); if (Response.Content != null) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } } return(Response); } case HttpStatusCode.NotFound: { //No need to process _format as not returning a body of type resource return(Response); } case HttpStatusCode.PreconditionFailed: { return(Response); } case HttpStatusCode.NotModified: { return(Response); } case HttpStatusCode.UnsupportedMediaType: { return(Response); } default: { if (Resource != null) { return(Response); } else { var OpOutComeIssueComp = new OperationOutcome.IssueComponent { Severity = OperationOutcome.IssueSeverity.Fatal, Code = OperationOutcome.IssueType.Exception, Diagnostics = "Internal Server Error: An unexpected HttpStatusCode has been encountered with a null resource to return. This is most likely a server bug." }; var OpOutCome = new OperationOutcome(); OpOutCome.Issue.Add(OpOutComeIssueComp); FhirOperationOutcomeSupport.EscapeOperationOutComeContent(OpOutCome); IFhirResourceNarrative.CreateNarrative(OpOutCome); if (_FhirMediaTypeFormatter != null) { Response.Content = new ObjectContent(typeof(Resource), OpOutCome, _FhirMediaTypeFormatter); } else { Response = Request.CreateResponse(_HttpStatusCode, OpOutCome); } return(Response); } } } }
public IResourceServiceOutcome Transact(Resource Resource, IRequestMeta RequestMeta) { if (IResourceServices == null) { throw new ArgumentNullException("IResourceServices can not be null."); } if (Resource == null) { throw new ArgumentNullException("Resource can not be null."); } if (RequestMeta == null) { throw new ArgumentNullException("RequestMeta can not be null."); } _RequestHeader = RequestMeta.RequestHeader ?? throw new ArgumentNullException("RequestHeaders can not be null."); _RequestUri = RequestMeta.PyroRequestUri ?? throw new ArgumentNullException("RequestUri can not be null."); _ServiceOperationOutcome = IResourceServiceOutcomeFactory.CreateResourceServiceOutcome(); _ServiceOperationOutcome.HttpStatusCode = System.Net.HttpStatusCode.OK; Bundle bundle = Resource as Bundle; if (bundle == null) { var Message = $"The FHIR server's service root endpoint can only accept 'Bundle' resources. Resource received was: {Resource.ResourceType.ToString()}"; var OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, Message); _ServiceOperationOutcome.ResourceResult = OpOutcome; _ServiceOperationOutcome.HttpStatusCode = System.Net.HttpStatusCode.Forbidden; _ServiceOperationOutcome.OperationType = Enum.RestEnum.CrudOperationType.Create; return(_ServiceOperationOutcome); } else if (bundle.Type != Bundle.BundleType.Transaction || (bundle.Type != Bundle.BundleType.Transaction && bundle.Type != Bundle.BundleType.Batch)) { var Message = $"The FHIR server's service root endpoint can only accept Bundle resources of Bundle.type = 'Transaction' or Bundle.type = 'Batch'. Type found was {bundle.Type.ToString()}"; var OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, Message); _ServiceOperationOutcome.ResourceResult = OpOutcome; _ServiceOperationOutcome.HttpStatusCode = System.Net.HttpStatusCode.Forbidden; _ServiceOperationOutcome.OperationType = Enum.RestEnum.CrudOperationType.Create; return(_ServiceOperationOutcome); } else { bundle.Type = Bundle.BundleType.TransactionResponse; bundle.Id = Guid.NewGuid().ToString(); if (bundle.Meta == null) { bundle.Meta = new Meta(); } bundle.Meta.LastUpdated = DateTimeOffset.Now; //FHIR Spec: http://build.fhir.org/bundle.html#transaction // If there is no request element, then there SHALL be a resource and the server must infer // whether this is a create or an update from the resource identity supplied. // NO Request Processing foreach (var NoRequestEntry in bundle.Entry.Where(x => x.Request == null || !x.Request.Method.HasValue)) { NoRequestEntry.Request = GenerateRequestComponentForEntry(NoRequestEntry); } //All entries with a Request, should be all at this point var EntryWithRequestList = bundle.Entry.Where(x => x.Request != null && x.Request.Method.HasValue); var EntryWithResourceAndRequestList = EntryWithRequestList.Where(x => x.Resource != null); var DeleteEntries = EntryWithRequestList.Where(x => x.Request.Method == Bundle.HTTPVerb.DELETE); var POSTEntries = EntryWithResourceAndRequestList.Where(x => x.Request.Method == Bundle.HTTPVerb.POST); var PUTEntries = EntryWithResourceAndRequestList.Where(x => x.Request.Method == Bundle.HTTPVerb.PUT); var GETEntries = EntryWithRequestList.Where(x => x.Request.Method == Bundle.HTTPVerb.GET); try { //DELETE Processing for (int i = 0; i < DeleteEntries.Count(); i++) { if (!DeleteProcessing(DeleteEntries.ElementAt(i), i)) { return(_ServiceOperationOutcome); } } //Assign new id's for POSTs and then update all POST and PUT entrie referances AssignResourceIdsAndUpdateReferances(POSTEntries, PUTEntries); //POST Processing for (int i = 0; i < POSTEntries.Count(); i++) { if (!PostProcessing(POSTEntries.ElementAt(i), i)) { _ServiceOperationOutcome.SuccessfulTransaction = false; return(_ServiceOperationOutcome); } } //PUT Processing for (int i = 0; i < PUTEntries.Count(); i++) { if (!PutProcessing(PUTEntries.ElementAt(i), i)) { _ServiceOperationOutcome.SuccessfulTransaction = false; return(_ServiceOperationOutcome); } } //GET Processing for (int i = 0; i < GETEntries.Count(); i++) { if (!GetProcessing(GETEntries.ElementAt(i), i)) { _ServiceOperationOutcome.SuccessfulTransaction = false; return(_ServiceOperationOutcome); } } _ServiceOperationOutcome.ResourceResult = bundle; _ServiceOperationOutcome.HttpStatusCode = System.Net.HttpStatusCode.OK; _ServiceOperationOutcome.OperationType = Enum.RestEnum.CrudOperationType.Update; _ServiceOperationOutcome.SuccessfulTransaction = true; } catch (Exception Exec) { throw new PyroException(System.Net.HttpStatusCode.InternalServerError, FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Exception, Exec.Message), Exec.Message); } } return(_ServiceOperationOutcome); }
private bool PostProcessing(Bundle.EntryComponent PostEntry, int PostEntryIndex) { IRequestMeta RequestMeta = IRequestMetaFactory.CreateRequestMeta().Set(PostEntry.Request); if (RequestMeta.PyroRequestUri.FhirRequestUri.IsOperation) { var Message = $"The FHIR server does not support the use of Operations within Transaction Bundles, found Operation request type of : '{RequestMeta.PyroRequestUri.FhirRequestUri.OperationName}'."; var OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Error, OperationOutcome.IssueType.Invalid, Message); _ServiceOperationOutcome.ResourceResult = OpOutcome; _ServiceOperationOutcome.HttpStatusCode = System.Net.HttpStatusCode.Forbidden; _ServiceOperationOutcome.OperationType = Enum.RestEnum.CrudOperationType.Create; return(false); } IPyroFhirUri ResourceIdToForce = IPyroFhirUriFactory.CreateFhirRequestUri(); if (String.IsNullOrEmpty(PostEntry.FullUrl)) { //Assgin a new GUID as there is not FullURL GUID to lookup from refererancing ResourceIdToForce.Parse($"{PostEntry.Resource.TypeName}/{Common.Tools.FhirGuid.FhirGuid.NewFhirGuid()}"); } else { //Use the new Resource id that we assigned when updating all referances by looking it up in the GetUUIDfromFullURL dic ResourceIdToForce.Parse(OldNewResourceReferanceMap[GetUUIDfromFullURL(PostEntry.FullUrl)]); } //Remove the Resource Id in the resource as this is a POST and no id should be present in the resource, we do force the new id given this is a transaction operation if (!String.IsNullOrEmpty(PostEntry.Resource.Id)) { PostEntry.Resource.Id = String.Empty; } IResourceServiceOutcome ResourceServiceOutcome = IResourceServices.Post(PostEntry.Resource, RequestMeta, ResourceIdToForce.ResourceId); if (ResourceServiceOutcome.SuccessfulTransaction) { PostEntry.FullUrl = CreateFullUrl(ResourceServiceOutcome); PostEntry.Response = new Bundle.ResponseComponent(); PostEntry.Response.Status = FormatHTTPStatusCodeAsString(ResourceServiceOutcome.HttpStatusCode); if (ResourceServiceOutcome.ResourceResult != null) { if (ResourceServiceOutcome.ResourceResult.ResourceType == ResourceType.OperationOutcome) { PostEntry.Response.Outcome = ResourceServiceOutcome.ResourceResult; } else { PostEntry.Resource = ResourceServiceOutcome.ResourceResult; } } if (ResourceServiceOutcome.LastModified != null) { PostEntry.Response.Etag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber).ToString(); PostEntry.Response.LastModified = ResourceServiceOutcome.LastModified; } PostEntry.Response.Status = $"{((int)ResourceServiceOutcome.HttpStatusCode).ToString()} {ResourceServiceOutcome.HttpStatusCode.ToString()}"; PostEntry.Response.Location = FormatResponseLocation(ResourceServiceOutcome.RequestUri.OriginalString, ResourceServiceOutcome.FhirResourceId, ResourceServiceOutcome.ResourceVersionNumber); return(true); } else { if (ResourceServiceOutcome.ResourceResult != null && ResourceServiceOutcome.ResourceResult is OperationOutcome Op) { IdentifieBatchEntityToClient(Op, PostEntry.FullUrl, "POST", PostEntryIndex); } _ServiceOperationOutcome = ResourceServiceOutcome; return(false); } }
public static HttpResponseMessage GetHttpResponseMessage(IResourceServiceOutcome ResourceServiceOutcome, HttpRequestMessage Request, Hl7.Fhir.Rest.SummaryType?SummaryType) { HttpStatusCode HttpStatusCode = ResourceServiceOutcome.HttpStatusCode; Resource Resource = ResourceServiceOutcome.ResourceResult; Pyro.Common.Formatters.FhirMediaTypeFormatter FhirMediaTypeFormatter = null; HttpResponseMessage Response = Request.CreateResponse(HttpStatusCode); if (Resource != null) { if (ResourceServiceOutcome.FormatMimeType == null) { Response = Request.CreateResponse(HttpStatusCode, Resource); } else { Pyro.Common.Formatters.FhirMediaTypeFormatter Formater = GetFhirMediaFormatter(Request, ResourceServiceOutcome.FormatMimeType); if (Formater != null) { FhirMediaTypeFormatter = Formater; Response = new HttpResponseMessage(HttpStatusCode); Response.Content = new ObjectContent(typeof(Resource), Resource, Formater); } else { Response = Request.CreateResponse(HttpStatusCode, Resource); } } //Annotate the Resource with the _summary, will get the annotation in MediaTypeFormatter XML or JSON if (Resource is Hl7.Fhir.Utility.IAnnotatable AnnotatableResource) { if (SummaryType.HasValue) { AnnotatableResource.AddAnnotation(SummaryType.Value); } else { AnnotatableResource.AddAnnotation(Hl7.Fhir.Rest.SummaryType.False); } } } //OK: 200 if (HttpStatusCode == HttpStatusCode.OK) { if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Read) { //LastModified Header & ETagVersion &Location Header if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); if (Response.Content != null && ResourceServiceOutcome.IsDeleted.HasValue && !ResourceServiceOutcome.IsDeleted.Value) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Update) { //Location Header if (Response.Content != null) { Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri); } //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Delete && ResourceServiceOutcome.ResourceVersionNumber != null) { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } else if (ResourceServiceOutcome.OperationType == RestEnum.CrudOperationType.Create) { //LastModified Header && ETag Version if (ResourceServiceOutcome.LastModified != null) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); //If we have a conditional Create where the Resource is found then we return OK but no Resource so no Content if (Response.Content != null) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } } return(Response); } else { var oIssueComponent = new OperationOutcome.IssueComponent(); oIssueComponent.Severity = OperationOutcome.IssueSeverity.Fatal; oIssueComponent.Code = OperationOutcome.IssueType.Exception; oIssueComponent.Diagnostics = "Internal Error. FhirRestResponse contains no FHIR Resource or Id."; var oOperationOutcome = new OperationOutcome(); oOperationOutcome.Issue = new List <OperationOutcome.IssueComponent>() { oIssueComponent }; throw new PyroException(HttpStatusCode.InternalServerError, oOperationOutcome, "Internal Error. FhirRestResponse contains no FHIR Resource or Id."); } } //Created: 201 else if (HttpStatusCode == HttpStatusCode.Created) { //Location Header if (Response.Content != null) { Response.Headers.Location = HttpHeaderSupport.AddResponseLocation(Request.RequestUri, ResourceServiceOutcome.FhirResourceId); } //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } return(Response); } //Gone: 410 - Search for a resource that no longer there, it is deleted or has never existed. else if (HttpStatusCode == HttpStatusCode.Gone) { //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); //Support.HttpHeaderSupport.AddLastModified(Response, oPyroServiceOperationOutcome.LastModified.Value); } return(Response); } //No Content: 204 else if (HttpStatusCode == HttpStatusCode.NoContent) { //LastModified Header && ETagVersion if (ResourceServiceOutcome.LastModified.HasValue) { Response.Headers.ETag = HttpHeaderSupport.GetEntityTagHeaderValueFromVersion(ResourceServiceOutcome.ResourceVersionNumber); if (Response.Content != null) { Response.Content.Headers.LastModified = ResourceServiceOutcome.LastModified; } } return(Response); } else if (HttpStatusCode == System.Net.HttpStatusCode.NotFound) { //No need to process _format as not returning a body of type resource return(Response); } else if (HttpStatusCode == System.Net.HttpStatusCode.PreconditionFailed) { return(Response); } else if (HttpStatusCode == System.Net.HttpStatusCode.NotModified) { return(Response); } //Forbidden: 403..and others else { if (Resource != null) { return(Response); } else { var OpOutComeIssueComp = new OperationOutcome.IssueComponent(); OpOutComeIssueComp.Severity = OperationOutcome.IssueSeverity.Fatal; OpOutComeIssueComp.Code = OperationOutcome.IssueType.Exception; OpOutComeIssueComp.Diagnostics = "Internal Server Error: An unexpected HttpStatusCode has been encountered with a null resource to return. This is most likely a server bug."; var OpOutCome = new OperationOutcome(); OpOutCome.Issue.Add(OpOutComeIssueComp); FhirOperationOutcomeSupport.EscapeOperationOutComeContent(OpOutCome); if (FhirMediaTypeFormatter != null) { Response.Content = new ObjectContent(typeof(Resource), OpOutCome, FhirMediaTypeFormatter); } else { Response = Request.CreateResponse(HttpStatusCode, OpOutCome); } return(Response); } } }
public static Bundle CreateBundle(ICollection <DtoResource> ResourceList, Bundle.BundleType BundleType, IPyroRequestUri RequestUri, int SearchTotal, int PagesTotal, int PageRequested, Paging.IPagingSupport IPagingSupport, Uri SearchPerformedUri = null) { var FhirBundle = new Bundle() { Type = Bundle.BundleType.Searchset }; FhirBundle.Type = BundleType; FhirBundle.Total = SearchTotal; //Paging IPagingSupport.SetBundlePagnation(FhirBundle, RequestUri.FhirRequestUri.OriginalString, PagesTotal, PageRequested, SearchPerformedUri); foreach (DtoResource DtoResource in ResourceList) { Bundle.EntryComponent oResEntry = new Bundle.EntryComponent(); if (DtoResource.IsDeleted == false) { try { oResEntry.Resource = FhirResourceSerializationSupport.DeSerializeFromXml(DtoResource.Xml); } catch (Exception oExec) { string Message = string.Format("Internal Server Error: Serialization of a Resource retrieved from the servers database failed. The record details were: Key: {0}, ResourceVersion: {1}, Received: {2}. The parser exception error was '{3}", DtoResource.FhirId, DtoResource.Version, DtoResource.Received.ToString(), oExec.Message); OperationOutcome OpOutcome = FhirOperationOutcomeSupport.Create(OperationOutcome.IssueSeverity.Fatal, OperationOutcome.IssueType.Exception, Message); throw new PyroException(System.Net.HttpStatusCode.InternalServerError, OpOutcome, Message); } } oResEntry.FullUrl = string.Join("/", RequestUri.FhirRequestUri.UriPrimaryServiceRoot.OriginalString, DtoResource.ResourceType.GetLiteral(), DtoResource.FhirId); if (BundleType == Bundle.BundleType.History) { oResEntry.FullUrl = string.Join("/", oResEntry.FullUrl); if (DtoResource.ResourceType.HasValue && DtoResource.ResourceType.HasValue) { oResEntry.Request = new Bundle.RequestComponent(); oResEntry.Request.Method = DtoResource.Method; switch (DtoResource.Method) { case Bundle.HTTPVerb.GET: oResEntry.Request.Url = string.Join("/", ModelInfo.FhirTypeToFhirTypeName(DtoResource.ResourceType.Value), DtoResource.FhirId, "_history", DtoResource.Version); break; case Bundle.HTTPVerb.POST: oResEntry.Request.Url = string.Join("/", ModelInfo.FhirTypeToFhirTypeName(DtoResource.ResourceType.Value), DtoResource.FhirId, "_history", DtoResource.Version); break; case Bundle.HTTPVerb.PUT: oResEntry.Request.Url = string.Join("/", ModelInfo.FhirTypeToFhirTypeName(DtoResource.ResourceType.Value), DtoResource.FhirId, "_history", DtoResource.Version); break; case Bundle.HTTPVerb.DELETE: oResEntry.Request.Url = string.Join("/", ModelInfo.FhirTypeToFhirTypeName(DtoResource.ResourceType.Value), DtoResource.FhirId, "_history", DtoResource.Version); break; default: throw new System.ComponentModel.InvalidEnumArgumentException(DtoResource.Method.ToString(), (int)DtoResource.Method, typeof(Bundle.HTTPVerb)); } } } if (BundleType == Bundle.BundleType.Searchset) { oResEntry.Search = new Bundle.SearchComponent(); oResEntry.Search.Mode = Bundle.SearchEntryMode.Match; if (DtoResource is DtoIncludeResource) { oResEntry.Search.Mode = Bundle.SearchEntryMode.Include; } oResEntry.Link = new List <Bundle.LinkComponent>(); } FhirBundle.Entry.Add(oResEntry); } return(FhirBundle); }