public async Task <IActionResult> ValidateById([FromRoute] string typeParameter, [FromRoute] string idParameter, [FromQuery] string profile) { Uri profileUri = GetProfile(profile); // Read resource from storage. RawResourceElement response = await _mediator.GetResourceAsync(new ResourceKey(typeParameter, idParameter), HttpContext.RequestAborted); // Convert it to fhir object. var resource = _resourceDeserializer.Deserialize(response); return(await RunValidationAsync(resource, profileUri)); }
public ResourceElement CreateSearchBundle(SearchResult result) { EnsureArg.IsNotNull(result, nameof(result)); // Create the bundle from the result. var bundle = new Bundle(); if (result != null) { IEnumerable <Bundle.EntryComponent> entries = result.Results.Select(r => { ResourceElement resource = _deserializer.Deserialize(r); return(new Bundle.EntryComponent { FullUrlElement = new FhirUri(_urlResolver.ResolveResourceUrl(resource)), Resource = resource.Instance.ToPoco <Resource>(), Search = new Bundle.SearchComponent { // TODO: For now, everything returned is a match. We will need to // distinct between match and include once we support it. Mode = Bundle.SearchEntryMode.Match, }, }); }); bundle.Entry.AddRange(entries); if (result.ContinuationToken != null) { bundle.NextLink = _urlResolver.ResolveRouteUrl( result.UnsupportedSearchParameters, result.ContinuationToken); } } // Add the self link to indicate which search parameters were used. bundle.SelfLink = _urlResolver.ResolveRouteUrl(result.UnsupportedSearchParameters); bundle.Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId; bundle.Type = Bundle.BundleType.Searchset; bundle.Total = result?.TotalCount; bundle.Meta = new Meta { LastUpdated = Clock.UtcNow, }; return(bundle.ToResourceElement()); }
public async Task <List <Tuple <string, string> > > GetGroupMembers(string groupId, DateTimeOffset groupMembershipTime, CancellationToken cancellationToken) { var groupResource = await _fhirDataStore.Value.GetAsync(new ResourceKey(KnownResourceTypes.Group, groupId), cancellationToken); if (groupResource == null) { throw new ResourceNotFoundException($"Group {groupId} was not found."); } var group = _resourceDeserializer.Deserialize(groupResource); var groupContents = group.ToPoco <Group>().Member; var members = new List <Tuple <string, string> >(); foreach (Group.MemberComponent member in groupContents) { var fhirGroupMembershipTime = new FhirDateTime(groupMembershipTime); if ( (member.Inactive == null || member.Inactive == false) && (member.Period?.EndElement == null || member.Period?.EndElement > fhirGroupMembershipTime) && (member.Period?.StartElement == null || member.Period?.StartElement < fhirGroupMembershipTime)) { var element = _referenceToElementResolver.Resolve(member.Entity.Reference); string id = (string)element.Children("id").First().Value; string resourceType = element.InstanceType; members.Add(Tuple.Create(id, resourceType)); } } return(members); }
private T GetResourceDefinition <T>(TagResourceReference resourceReference) { var tagResource = GetPageableResource(resourceReference).Resource; T result; byte[] resourceDefinitionData = tagResource.DefinitionData; ApplyResourceDefinitionFixups(tagResource, resourceDefinitionData); byte[] resourceRawData = GetResourceData(resourceReference); if (resourceRawData == null) { resourceRawData = new byte[0]; } // deserialize the resource definition again using (var definitionDataStream = new MemoryStream(resourceDefinitionData)) using (var definitionDataReader = new EndianReader(definitionDataStream, EndianFormat.LittleEndian)) using (var dataStream = new MemoryStream(resourceRawData)) using (var dataReader = new EndianReader(dataStream, EndianFormat.LittleEndian)) { var context = new ResourceDefinitionSerializationContext(dataReader, definitionDataReader, tagResource.DefinitionAddress.Type); var deserializer = new ResourceDeserializer(Cache.Version); // deserialize without access to the data definitionDataReader.SeekTo(tagResource.DefinitionAddress.Offset); result = deserializer.Deserialize <T>(context); } return(result); }
public void DeserializesHasManyRelationship() { var target = new ResourceDeserializer(_singleJson, typeof(Person)); var result = target.Deserialize() as Person; var expectedFriend = _person.Friends.Single(); var actualFriend = result?.Friends.Single(); Assert.Equal(expectedFriend.Identifier, actualFriend?.Identifier); Assert.Null(actualFriend?.FirstName); Assert.Null(actualFriend?.LastName); Assert.Equal(default(int?), actualFriend?.Age); Assert.Null(actualFriend?.Job); Assert.Null(actualFriend?.Friends); var expectedFamilyMembers = _person.FamilyMembers.ToArray(); var actualFamilyMembers = result?.FamilyMembers?.ToArray(); Assert.NotNull(actualFamilyMembers); Assert.Equal(expectedFamilyMembers.Length, actualFamilyMembers.Length); expectedFamilyMembers.Zip(actualFamilyMembers, (expected, actual) => { Assert.Equal(expected.Identifier, actual.Identifier); return(true); }); }
public void ValidatesTopLevelMemberExclusivity() { var content = FileAsJson("invalid-top-level.json"); var target = new ResourceDeserializer(content, typeof(Person)); Assert.Throws <JsonApiException>(() => target.Deserialize()); }
public void ValidatesRootIsObject() { var content = FileAsJson("no-object-root.json"); var target = new ResourceDeserializer(content, typeof(Person)); Assert.Throws <JsonApiException>(() => target.Deserialize()); }
public void ValidatesMutuallyExclusiveTopLevelMembers() { var content = FileAsJson("errors-and-data.json"); var target = new ResourceDeserializer(content, typeof(Person)); Assert.Throws <JsonApiException>(() => target.Deserialize()); }
public void ValidatesNoIncludedWithoutData() { var content = FileAsJson("included-no-data.json"); var target = new ResourceDeserializer(content, typeof(Person)); Assert.Throws <JsonApiException>(() => target.Deserialize()); }
public void ValidatesRequiredTopLevelMembers() { var content = FileAsJson("no-data-errors-meta.json"); var target = new ResourceDeserializer(content, typeof(Person)); Assert.Throws <JsonApiException>(() => target.Deserialize()); }
public void GivenARawResourceOfUnknownType_WhenDeserializing_ThenANotSupportedExceptionIsThrown() { var raw = new RawResource("{}", ResourceFormat.Unknown); var wrapper = new ResourceWrapper("id1", "version1", "Observation", raw, new ResourceRequest("http://fhir", HttpMethod.Post), Clock.UtcNow, false, null, null); Assert.Throws <NotSupportedException>(() => ResourceDeserializer.Deserialize(wrapper)); }
/// <summary> /// For each result in a batch of resources this will extract new search params /// Then compare those to the old values to determine if an update is needed /// Needed updates will be committed in a batch /// </summary> /// <param name="results">The resource batch to process</param> /// <param name="resourceTypeSearchParameterHashMap">Map of resource type to current hash value of the search parameters for that resource type</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>A Task</returns> public async Task ProcessSearchResultsAsync(SearchResult results, IReadOnlyDictionary <string, string> resourceTypeSearchParameterHashMap, CancellationToken cancellationToken) { EnsureArg.IsNotNull(results, nameof(results)); EnsureArg.IsNotNull(resourceTypeSearchParameterHashMap, nameof(resourceTypeSearchParameterHashMap)); var updateSearchIndices = new List <ResourceWrapper>(); foreach (var entry in results.Results) { if (!resourceTypeSearchParameterHashMap.TryGetValue(entry.Resource.ResourceTypeName, out string searchParamHash)) { searchParamHash = string.Empty; } entry.Resource.SearchParameterHash = searchParamHash; var resourceElement = _deserializer.Deserialize(entry.Resource); var newIndices = _searchIndexer.Extract(resourceElement); // TODO: If it reasonable to do so, we can compare // old and new search indices to avoid unnecessarily updating search indices // when not changes have been made. entry.Resource.UpdateSearchIndices(newIndices); updateSearchIndices.Add(entry.Resource); if (cancellationToken.IsCancellationRequested) { return; } } using (IScoped <IFhirDataStore> store = _fhirDataStoreFactory()) { await store.Value.UpdateSearchParameterIndicesBatchAsync(updateSearchIndices, cancellationToken); } }
/// <summary> /// For each result in a batch of resources this will extract new search params /// Then compare those to the old values to determine if an update is needed /// Needed updates will be committed in a batch /// </summary> /// <param name="results">The resource batch to process</param> /// <param name="searchParamHash">the current hash value of the search parameters</param> /// <param name="cancellationToken">Cancellation token</param> /// <returns>A Task</returns> public async Task ProcessSearchResultsAsync(SearchResult results, string searchParamHash, CancellationToken cancellationToken) { var updateHashValueOnly = new List <ResourceWrapper>(); var updateSearchIndices = new List <ResourceWrapper>(); foreach (var entry in results.Results) { entry.Resource.SearchParameterHash = searchParamHash; var resourceElement = _deserializer.Deserialize(entry.Resource); var newIndices = _searchIndexer.Extract(resourceElement); var newIndicesHash = new HashSet <SearchIndexEntry>(newIndices); var prevIndicesHash = new HashSet <SearchIndexEntry>(entry.Resource.SearchIndices); if (newIndicesHash.SetEquals(prevIndicesHash)) { updateHashValueOnly.Add(entry.Resource); } else { entry.Resource.UpdateSearchIndices(newIndices); updateSearchIndices.Add(entry.Resource); } } using (IScoped <IFhirDataStore> store = _fhirDataStoreFactory()) { await store.Value.UpdateSearchParameterHashBatchAsync(updateHashValueOnly, cancellationToken); await store.Value.UpdateSearchParameterIndicesBatchAsync(updateSearchIndices, cancellationToken); } }
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) { EnsureArg.IsNotNull(context, nameof(context)); EnsureArg.IsNotNull(selectedEncoding, nameof(selectedEncoding)); context.HttpContext.AllowSynchronousIO(); Resource resourceObject = null; if (typeof(RawResourceElement).IsAssignableFrom(context.ObjectType)) { resourceObject = _deserializer.Deserialize(context.Object as RawResourceElement).ToPoco <Resource>(); } else if (typeof(Hl7.Fhir.Model.Bundle).IsAssignableFrom(context.ObjectType)) { // Need to set Resource property for resources in entries var bundle = context.Object as Hl7.Fhir.Model.Bundle; foreach (var entry in bundle.Entry.Where(x => x is RawBundleEntryComponent)) { var rawResource = entry as RawBundleEntryComponent; entry.Resource = _deserializer.Deserialize(rawResource.ResourceElement).ToPoco <Resource>(); } resourceObject = bundle; } else { resourceObject = (Resource)context.Object; } HttpResponse response = context.HttpContext.Response; using (TextWriter textWriter = context.WriterFactory(response.Body, selectedEncoding)) using (var writer = new XmlTextWriter(textWriter)) { if (context.HttpContext.GetPrettyOrDefault()) { writer.Formatting = Formatting.Indented; } _fhirXmlSerializer.Serialize(resourceObject, writer, context.HttpContext.GetSummaryTypeOrDefault(), elements: context.HttpContext.GetElementsOrDefault()); } return(Task.CompletedTask); }
public ResourceElement CreateSearchBundle(SearchResult result) { return(CreateBundle(result, Bundle.BundleType.Searchset, r => { ResourceElement resource = _deserializer.Deserialize(r.Resource); return new Bundle.EntryComponent { FullUrlElement = new FhirUri(_urlResolver.ResolveResourceUrl(resource)), Resource = resource.Instance.ToPoco <Resource>(), Search = new Bundle.SearchComponent { Mode = r.SearchEntryMode == SearchEntryMode.Match ? Bundle.SearchEntryMode.Match : Bundle.SearchEntryMode.Include, }, }; })); }
/// <summary> /// Converts json into an object of a specified type /// </summary> /// <param name="object">Json to convert</param> /// <param name="type">Type to convert to</param> /// <param name="config">The configuration to be used for deserialization</param> /// <returns>Json converted into the specified type of object</returns> public object Deserialize(JToken @object, Type type, JsonApiConfiguration config = null) { if (config == null) { config = new JsonApiConfiguration(); } var target = new ResourceDeserializer(@object, type, config.PropertyNameConverter); return target.Deserialize(); }
public void DeserializesBelongsToRelationships() { var target = new ResourceDeserializer(_singleJson, typeof(Person)); var result = target.Deserialize() as Person; var job = result?.Job; Assert.Equal(_person.Job.Id, job?.Id); Assert.Null(job?.Name); Assert.Equal(0, job?.NumberOfEmployees); }
public void DeserializesAttributes() { var target = new ResourceDeserializer(_singleJson, typeof(Person)); var result = target.Deserialize() as Person; Assert.Equal(_person.Identifier, result?.Identifier); Assert.Equal(_person.FirstName, result?.FirstName); Assert.Equal(_person.LastName, result?.LastName); Assert.Equal(_person.Age, result?.Age); }
public void DeserializesBelongsToRelationshipsWithNullData() { var person = JToken.Parse("{'data': {'relationships': {'job': {data: null}}}}"); var target = new ResourceDeserializer(person, typeof(Person)); var result = target.Deserialize() as Person; var job = result?.Job; Assert.Null(job); }
public ResourceElement CreateSearchBundle(SearchResult result) { return(CreateBundle(result, Bundle.BundleType.Searchset, r => { ResourceElement resource = _deserializer.Deserialize(r); return new Bundle.EntryComponent { FullUrlElement = new FhirUri(_urlResolver.ResolveResourceUrl(resource)), Resource = resource.Instance.ToPoco <Resource>(), Search = new Bundle.SearchComponent { // TODO: For now, everything returned is a match. We will need to // distinct between match and include once we support it. Mode = Bundle.SearchEntryMode.Match, }, }; })); }
public void DeserializesEnumerables() { var target = new ResourceDeserializer(_collectionJson, typeof(Person[])); var result = target.Deserialize() as Person[]; Assert.Equal(_people.Length, result?.Length); for (var i = 0; i < _people.Length; i++) { Assert.Equal(_people[i].Identifier, result?[i].Identifier); } }
public void DeserializesWithoutId() { (_singleJson["data"] as JObject)?.Property("id").Remove(); var target = new ResourceDeserializer(_singleJson, typeof(Person)); var result = target.Deserialize() as Person; Assert.Equal(null, result?.Identifier); Assert.Equal(_person.FirstName, result?.FirstName); Assert.Equal(_person.LastName, result?.LastName); Assert.Equal(_person.Age, result?.Age); }
public void GivenARawResource_WhenDeserializingFromJson_ThenTheObjectIsReturned() { var observation = Samples.GetDefaultObservation(); observation.Id = "id1"; var wrapper = new ResourceWrapper(observation, _rawResourceFactory.Create(observation), new ResourceRequest("http://fhir", HttpMethod.Post), false, null, null); var newObject = ResourceDeserializer.Deserialize(wrapper); Assert.Equal(observation.Id, newObject.Id); Assert.Equal(observation.VersionId, newObject.VersionId); }
public Bundle CreateHistoryBundle(IEnumerable <Tuple <string, string> > unsupportedSearchParams, SearchResult result) { // Create the bundle from the result. var bundle = new Bundle(); if (result != null) { IEnumerable <Bundle.EntryComponent> entries = result.Results.Select(r => { Resource resource = ResourceDeserializer.Deserialize(r); var hasVerb = Enum.TryParse(r.Request?.Method, true, out Bundle.HTTPVerb httpVerb); return(new Bundle.EntryComponent { FullUrlElement = new FhirUri(_urlResolver.ResolveResourceUrl(resource, true)), Resource = resource, Request = new Bundle.RequestComponent { Method = hasVerb ? (Bundle.HTTPVerb?)httpVerb: null, Url = r.Request?.Url?.ToString(), }, Response = new Bundle.ResponseComponent { LastModified = r.LastModified, Etag = WeakETag.FromVersionId(r.Version).ToString(), }, }); }); bundle.Entry.AddRange(entries); if (result.ContinuationToken != null) { bundle.NextLink = _urlResolver.ResolveRouteUrl( _fhirRequestContextAccessor.FhirRequestContext.RouteName, unsupportedSearchParams, result.ContinuationToken); } } // Add the self link to indicate which search parameters were used. bundle.SelfLink = _urlResolver.ResolveRouteUrl(_fhirRequestContextAccessor.FhirRequestContext.RouteName, unsupportedSearchParams); bundle.Id = _fhirRequestContextAccessor.FhirRequestContext.CorrelationId; bundle.Type = Bundle.BundleType.History; bundle.Total = result?.TotalCount; bundle.Meta = new Meta { LastUpdated = Clock.UtcNow, }; return(bundle); }
public void DeserializesHasManyRelationship() { var target = new ResourceDeserializer(_singleJson, typeof(Person)); var result = target.Deserialize() as Person; var expected = _person.Friends.Single(); var actual = result?.Friends.Single(); Assert.Equal(expected.Identifier, actual?.Identifier); Assert.Null(actual?.FirstName); Assert.Null(actual?.LastName); Assert.Equal(default(int?), actual?.Age); Assert.Null(actual?.Job); Assert.Null(actual?.Friends); }
public void DeserializesCamelCase() { var camelCasePropertyNameConverter = new CamelCasePropertyNameConverter(); var singleSerializer = new ResourceSerializer( _person, new PersonResource(), new Uri("http://example.com/people/1"), new DefaultUrlPathBuilder(), null, null, camelCasePropertyNameConverter); var singleJson = JToken.Parse(singleSerializer.Serialize().ToString()); var target = new ResourceDeserializer(singleJson, typeof(Person), camelCasePropertyNameConverter); var result = target.Deserialize() as Person; Assert.Equal(_person.FirstName, result?.FirstName); Assert.Equal(_person.LastName, result?.LastName); }
public async Task <SearchResourceResponse> Handle(SearchResourceRequest request, CancellationToken cancellationToken, RequestHandlerDelegate <SearchResourceResponse> next) { EnsureArg.IsNotNull(request, nameof(request)); Tuple <string, string> listParameter = request.Queries .FirstOrDefault(x => string.Equals(x.Item1, KnownQueryParameterNames.List, StringComparison.OrdinalIgnoreCase)); // if _list was not requested, or _list was requested with invalid value, continue... if ((listParameter == null) || string.IsNullOrWhiteSpace(listParameter.Item2)) { return(await next()); } ResourceWrapper listWrapper = await _dataStore.Value.GetAsync(new ResourceKey <Hl7.Fhir.Model.List>(listParameter.Item2), cancellationToken); // wanted list was not found if (listWrapper == null) { return(CreateEmptySearchResponse(request)); } ResourceElement list = _deserializer.Deserialize(listWrapper); IEnumerable <ReferenceSearchValue> references = list.ToPoco <Hl7.Fhir.Model.List>() .Entry .Where(x => x.Deleted != true) .Select(x => _referenceSearchValueParser.Parse(x.Item.Reference)) .Where(x => string.IsNullOrWhiteSpace(request.ResourceType) || string.Equals(request.ResourceType, x.ResourceType, StringComparison.Ordinal)) .ToArray(); // the requested resource was not found in the list if (!references.Any()) { return(CreateEmptySearchResponse(request)); } // Remove the 'list' params from the queries, to be later replaced with specific ids of resources IEnumerable <Tuple <string, string> > query = request.Queries.Except(new[] { listParameter }); query = query.Concat(new[] { Tuple.Create(KnownQueryParameterNames.Id, string.Join(",", references.Select(x => x.ResourceId))) }); request.Queries = query.ToArray(); return(await next()); }
public async Task <PatchResourceResponse> Handle(PatchResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); if (await AuthorizationService.CheckAccess(DataActions.Write, cancellationToken) != DataActions.Write) { throw new UnauthorizedFhirActionException(); } var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } ResourceWrapper currentDoc = await FhirDataStore.GetAsync(key, cancellationToken); if (currentDoc == null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, key.ResourceType, key.Id)); } ResourceElement resource = _resourceDeserializer.Deserialize(currentDoc); Resource resourceInstance = resource.Instance.ToPoco <Resource>(); message.PatchDocument.ApplyTo(resourceInstance); ResourceWrapper resourceWrapper = CreateResourceWrapper(resourceInstance, deleted: false, keepMeta: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(currentDoc.ResourceTypeName, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( resourceWrapper, weakETag : message.WeakETag, allowCreate : false, keepHistory : keepHistory, cancellationToken : cancellationToken); resourceInstance.VersionId = result.Wrapper.Version; return(new PatchResourceResponse(new SaveOutcome(new RawResourceElement(result.Wrapper), result.OutcomeType))); }
public async Task <GetResourceResponse> Handle(GetResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); if (await AuthorizationService.CheckAccess(DataActions.Read) != DataActions.Read) { throw new UnauthorizedFhirActionException(); } var key = message.ResourceKey; var currentDoc = await FhirDataStore.GetAsync(key, cancellationToken); if (currentDoc == null) { if (string.IsNullOrEmpty(key.VersionId)) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundById, key.ResourceType, key.Id)); } else { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, key.ResourceType, key.Id, key.VersionId)); } } if (currentDoc.IsHistory && ConformanceProvider != null && await ConformanceProvider.Value.CanReadHistory(key.ResourceType, cancellationToken) == false) { throw new MethodNotAllowedException(string.Format(Core.Resources.ReadHistoryDisabled, key.ResourceType)); } if (currentDoc.IsDeleted) { // As per FHIR Spec if the resource was marked as deleted on that version or the latest is marked as deleted then // we need to return a resource gone message. throw new ResourceGoneException(new ResourceKey(currentDoc.ResourceTypeName, currentDoc.ResourceId, currentDoc.Version)); } return(new GetResourceResponse(_deserializer.Deserialize(currentDoc))); }
public void GivenAResource_WhenCreatingAResourceWrapper_ThenMetaPropertiesAreCorrect() { var observation = Samples.GetDefaultObservation(); observation.Id = "id1"; observation.VersionId = "version1"; observation.Meta.Profile = new List <string> { "test" }; var lastModified = new DateTimeOffset(2017, 1, 1, 1, 1, 1, TimeSpan.Zero); using (Mock.Property(() => Clock.UtcNowFunc, () => lastModified)) { var wrapper = new ResourceWrapper(observation, _rawResourceFactory.Create(observation), new ResourceRequest("http://fhir", HttpMethod.Post), false, null, null, null); var resource = ResourceDeserializer.Deserialize(wrapper); Assert.Equal(observation.VersionId, resource.Meta.VersionId); Assert.Equal(lastModified, resource.Meta.LastUpdated); Assert.Equal("test", resource.Meta.Profile.First()); } }