public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await AuthorizationService.CheckAccess(DataActions.Write, cancellationToken) != DataActions.Write) { throw new UnauthorizedFhirActionException(); } Resource resource = request.Resource.ToPoco <Resource>(); bool allowCreate = await ConformanceProvider.Value.CanUpdateCreate(resource.TypeName, cancellationToken); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); bool requireETagOnUpdate = await ConformanceProvider.Value.RequireETag(resource.TypeName, cancellationToken); ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false, keepMeta: allowCreate); UpsertOutcome result = await FhirDataStore.UpsertAsync(resourceWrapper, request.WeakETag, allowCreate, keepHistory, cancellationToken, requireETagOnUpdate); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(new RawResourceElement(result.Wrapper), result.OutcomeType))); }
public async Task <UpsertResourceResponse> Handle(PatchResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await AuthorizationService.CheckAccess(DataActions.Read | DataActions.Write, cancellationToken) != (DataActions.Read | DataActions.Write)) { throw new UnauthorizedFhirActionException(); } ResourceKey key = request.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)); } var patchedResource = _patchService.Patch(currentDoc, request.PatchDocument, request.WeakETag); return(await _mediator.Send <UpsertResourceResponse>(new UpsertResourceRequest(patchedResource), cancellationToken)); }
public IntegrationTestCosmosDataStore() { _cosmosDataStoreConfiguration = new CosmosDataStoreConfiguration { Host = Environment.GetEnvironmentVariable("CosmosDb:Host") ?? CosmosDbLocalEmulator.Host, Key = Environment.GetEnvironmentVariable("CosmosDb:Key") ?? CosmosDbLocalEmulator.Key, DatabaseId = Environment.GetEnvironmentVariable("CosmosDb:DatabaseId") ?? "FhirTests", AllowDatabaseCreation = true, PreferredLocations = Environment.GetEnvironmentVariable("CosmosDb:PreferredLocations")?.Split(';', StringSplitOptions.RemoveEmptyEntries), }; _cosmosCollectionConfiguration = new CosmosCollectionConfiguration { CollectionId = Guid.NewGuid().ToString(), }; var fhirStoredProcs = typeof(IFhirStoredProcedure).Assembly .GetTypes() .Where(x => !x.IsAbstract && typeof(IFhirStoredProcedure).IsAssignableFrom(x)) .ToArray() .Select(type => (IFhirStoredProcedure)Activator.CreateInstance(type)); var optionsMonitor = Substitute.For <IOptionsMonitor <CosmosCollectionConfiguration> >(); optionsMonitor.Get(CosmosDb.Constants.CollectionConfigurationName).Returns(_cosmosCollectionConfiguration); var updaters = new IFhirCollectionUpdater[] { new FhirCollectionSettingsUpdater(_cosmosDataStoreConfiguration, optionsMonitor, NullLogger <FhirCollectionSettingsUpdater> .Instance), new FhirStoredProcedureInstaller(fhirStoredProcs), }; var dbLock = new CosmosDbDistributedLockFactory(Substitute.For <Func <IScoped <IDocumentClient> > >(), NullLogger <CosmosDbDistributedLock> .Instance); var upgradeManager = new FhirCollectionUpgradeManager(updaters, _cosmosDataStoreConfiguration, optionsMonitor, dbLock, NullLogger <FhirCollectionUpgradeManager> .Instance); IDocumentClientTestProvider testProvider = new DocumentClientReadWriteTestProvider(); var documentClientInitializer = new DocumentClientInitializer(testProvider, NullLogger <DocumentClientInitializer> .Instance); _documentClient = documentClientInitializer.CreateDocumentClient(_cosmosDataStoreConfiguration); var fhirCollectionInitializer = new CollectionInitializer(_cosmosCollectionConfiguration.CollectionId, _cosmosDataStoreConfiguration, _cosmosCollectionConfiguration.InitialCollectionThroughput, upgradeManager, NullLogger <CollectionInitializer> .Instance); documentClientInitializer.InitializeDataStore(_documentClient, _cosmosDataStoreConfiguration, new List <ICollectionInitializer> { fhirCollectionInitializer }).GetAwaiter().GetResult(); var cosmosDocumentQueryFactory = new FhirCosmosDocumentQueryFactory(Substitute.For <IFhirRequestContextAccessor>(), NullFhirDocumentQueryLogger.Instance); var fhirRequestContextAccessor = new FhirRequestContextAccessor(); _dataStore = new FhirDataStore( new NonDisposingScope(_documentClient), _cosmosDataStoreConfiguration, cosmosDocumentQueryFactory, new RetryExceptionPolicyFactory(_cosmosDataStoreConfiguration), fhirRequestContextAccessor, optionsMonitor, NullLogger <FhirDataStore> .Instance); }
public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); DataActions requiredDataAction = request.DeleteOperation == DeleteOperation.SoftDelete ? DataActions.Delete : DataActions.HardDelete | DataActions.Delete; if (await AuthorizationService.CheckAccess(requiredDataAction, cancellationToken) != requiredDataAction) { throw new UnauthorizedFhirActionException(); } var key = request.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; switch (request.DeleteOperation) { case DeleteOperation.SoftDelete: var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(request.ResourceKey.ResourceType)); emptyInstance.Id = request.ResourceKey.Id; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true, keepMeta: false); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result?.Wrapper.Version; break; case DeleteOperation.HardDelete: case DeleteOperation.PurgeHistory: await FhirDataStore.HardDeleteAsync(key, request.DeleteOperation == DeleteOperation.PurgeHistory, cancellationToken); break; default: throw new ArgumentOutOfRangeException(nameof(request)); } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), weakETag: WeakETag.FromVersionId(version))); }
public FhirCosmosSearchService( ISearchOptionsFactory searchOptionsFactory, FhirDataStore fhirDataStore, IQueryBuilder queryBuilder, IBundleFactory bundleFactory) : base(searchOptionsFactory, bundleFactory, fhirDataStore) { EnsureArg.IsNotNull(fhirDataStore, nameof(fhirDataStore)); EnsureArg.IsNotNull(queryBuilder, nameof(queryBuilder)); _fhirDataStore = fhirDataStore; _queryBuilder = queryBuilder; }
public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; if (message.HardDelete) { await FhirDataStore.HardDeleteAsync(key, cancellationToken); } else { ResourceWrapper existing = await FhirDataStore.GetAsync(key, cancellationToken); version = existing?.Version; if (existing?.IsDeleted == false) { var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(existing.ResourceTypeName)); emptyInstance.Id = existing.ResourceId; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, WeakETag.FromVersionId(existing.Version), allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result.Wrapper.Version; } } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), WeakETag.FromVersionId(version))); }
public async Task <DeleteResourceResponse> Handle(DeleteResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); DataActions requiredDataAction = message.HardDelete ? DataActions.Delete | DataActions.HardDelete : DataActions.Delete; if (AuthorizationService.CheckAccess(requiredDataAction) != requiredDataAction) { throw new UnauthorizedFhirActionException(); } var key = message.ResourceKey; if (!string.IsNullOrEmpty(key.VersionId)) { throw new MethodNotAllowedException(Core.Resources.DeleteVersionNotAllowed); } string version = null; if (message.HardDelete) { await FhirDataStore.HardDeleteAsync(key, cancellationToken); } else { var emptyInstance = (Resource)Activator.CreateInstance(ModelInfo.GetTypeForFhirType(message.ResourceKey.ResourceType)); emptyInstance.Id = message.ResourceKey.Id; ResourceWrapper deletedWrapper = CreateResourceWrapper(emptyInstance, deleted: true); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(key.ResourceType, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( deletedWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); version = result?.Wrapper.Version; } if (string.IsNullOrWhiteSpace(version)) { return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id))); } return(new DeleteResourceResponse(new ResourceKey(key.ResourceType, key.Id, version), WeakETag.FromVersionId(version))); }
private async Task <UpsertOutcome> UpsertAsync(UpsertResourceRequest message, ResourceWrapper resourceWrapper, bool allowCreate, bool keepHistory, CancellationToken cancellationToken) { UpsertOutcome result; try { result = await FhirDataStore.UpsertAsync(resourceWrapper, message.WeakETag, allowCreate, keepHistory, cancellationToken); } catch (PreconditionFailedException) { throw new ResourceConflictException(message.WeakETag); } return(result); }
private async Task <UpsertOutcome> UpsertAsync(UpsertResourceRequest message, ResourceWrapper resourceWrapper, bool allowCreate, bool keepHistory, CancellationToken cancellationToken) { UpsertOutcome result; try { result = await FhirDataStore.UpsertAsync(resourceWrapper, message.WeakETag, allowCreate, keepHistory, cancellationToken); } catch (PreconditionFailedException) when(_modelInfoProvider.Version == FhirSpecification.Stu3) { // The backwards compatibility behavior of Stu3 is to return a Conflict instead of Precondition fail throw new ResourceConflictException(message.WeakETag); } return(result); }
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 request, CancellationToken cancellationToken) { EnsureArg.IsNotNull(request, nameof(request)); if (await AuthorizationService.CheckAccess(DataActions.Read, cancellationToken) != DataActions.Read) { throw new UnauthorizedFhirActionException(); } var key = request.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(new RawResourceElement(currentDoc))); }
public async Task <UpsertResourceResponse> Handle(UpsertResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); Resource resource = message.Resource; if (await ConformanceProvider.Value.RequireETag(resource.TypeName, cancellationToken) && message.WeakETag == null) { throw new PreconditionFailedException(string.Format(Core.Resources.IfMatchHeaderRequiredForResource, resource.TypeName)); } bool allowCreate = await ConformanceProvider.Value.CanUpdateCreate(resource.TypeName, cancellationToken); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false); UpsertOutcome result = await FhirDataStore.UpsertAsync(resourceWrapper, message.WeakETag, allowCreate, keepHistory, cancellationToken); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(resource, result.OutcomeType))); }
public async Task <UpsertResourceResponse> Handle(CreateResourceRequest message, CancellationToken cancellationToken) { EnsureArg.IsNotNull(message, nameof(message)); var resource = message.Resource.Instance.ToPoco <Resource>(); // If an Id is supplied on create it should be removed/ignored resource.Id = null; ResourceWrapper resourceWrapper = CreateResourceWrapper(resource, deleted: false); bool keepHistory = await ConformanceProvider.Value.CanKeepHistory(resource.TypeName, cancellationToken); UpsertOutcome result = await FhirDataStore.UpsertAsync( resourceWrapper, weakETag : null, allowCreate : true, keepHistory : keepHistory, cancellationToken : cancellationToken); resource.VersionId = result.Wrapper.Version; return(new UpsertResourceResponse(new SaveOutcome(resource.ToResourceElement(), SaveOutcomeType.Created))); }
private async Task <UpsertOutcome> UpsertAsync(UpsertResourceRequest message, ResourceWrapper resourceWrapper, bool allowCreate, bool keepHistory, CancellationToken cancellationToken) { return(await FhirDataStore.UpsertAsync(resourceWrapper, message.WeakETag, allowCreate, keepHistory, cancellationToken)); }