public ControlPlaneDataStore(
            IScoped<IDocumentClient> documentClient,
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            ICosmosDocumentQueryFactory cosmosDocumentQueryFactory,
            RetryExceptionPolicyFactory retryExceptionPolicyFactory,
            IOptionsMonitor<CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
            ILogger<ControlPlaneDataStore> logger)
        {
            EnsureArg.IsNotNull(documentClient, nameof(documentClient));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(cosmosDocumentQueryFactory, nameof(cosmosDocumentQueryFactory));
            EnsureArg.IsNotNull(retryExceptionPolicyFactory, nameof(retryExceptionPolicyFactory));
            EnsureArg.IsNotNull(namedCosmosCollectionConfigurationAccessor, nameof(namedCosmosCollectionConfigurationAccessor));
            EnsureArg.IsNotNull(logger, nameof(logger));

            var collectionConfig = namedCosmosCollectionConfigurationAccessor.Get(Constants.CollectionConfigurationName);

            _documentClient = documentClient;
            _collectionUri = cosmosDataStoreConfiguration.GetRelativeCollectionUri(collectionConfig.CollectionId);
            _retryExceptionPolicyFactory = retryExceptionPolicyFactory;
            _cosmosDocumentQueryFactory = cosmosDocumentQueryFactory;
            _logger = logger;
            _hardDeleteIdentityProvider = new HardDeleteIdentityProvider();
            _hardDeleteRole = new HardDeleteRole();
        }
Example #2
0
        /// <inheritdoc />
        public async Task InitializeDataStore(CosmosClient client, CosmosDataStoreConfiguration cosmosDataStoreConfiguration, IEnumerable <ICollectionInitializer> collectionInitializers)
        {
            EnsureArg.IsNotNull(client, nameof(client));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(collectionInitializers, nameof(collectionInitializers));

            try
            {
                _logger.LogInformation("Initializing Cosmos DB Database {DatabaseId} and collections", cosmosDataStoreConfiguration.DatabaseId);

                if (cosmosDataStoreConfiguration.AllowDatabaseCreation)
                {
                    _logger.LogInformation("CreateDatabaseIfNotExists {DatabaseId}", cosmosDataStoreConfiguration.DatabaseId);

                    await client.CreateDatabaseIfNotExistsAsync(
                        cosmosDataStoreConfiguration.DatabaseId,
                        cosmosDataStoreConfiguration.InitialDatabaseThroughput.HasValue?ThroughputProperties.CreateManualThroughput(cosmosDataStoreConfiguration.InitialDatabaseThroughput.Value) : null);
                }

                foreach (var collectionInitializer in collectionInitializers)
                {
                    await collectionInitializer.InitializeCollection(client);
                }

                _logger.LogInformation("Cosmos DB Database {DatabaseId} and collections successfully initialized", cosmosDataStoreConfiguration.DatabaseId);
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "Cosmos DB Database {DatabaseId} and collections initialization failed", cosmosDataStoreConfiguration.DatabaseId);
                throw;
            }
        }
Example #3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FhirDataStore"/> class.
        /// </summary>
        /// <param name="documentClient">
        /// A function that returns an <see cref="IDocumentClient"/>.
        /// Note that this is a function so that the lifetime of the instance is not directly controlled by the IoC container.
        /// </param>
        /// <param name="cosmosDataStoreConfiguration">The data store configuration</param>
        /// <param name="cosmosDocumentQueryFactory">The factory used to create the document query.</param>
        /// <param name="retryExceptionPolicyFactory">The retry exception policy factory.</param>
        /// <param name="fhirRequestContextAccessor">The fhir request context accessor.</param>
        /// <param name="namedCosmosCollectionConfigurationAccessor">The IOptions accessor to get a named version.</param>
        /// <param name="logger">The logger instance.</param>
        public FhirDataStore(
            IScoped <IDocumentClient> documentClient,
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            FhirCosmosDocumentQueryFactory cosmosDocumentQueryFactory,
            RetryExceptionPolicyFactory retryExceptionPolicyFactory,
            IFhirRequestContextAccessor fhirRequestContextAccessor,
            IOptionsMonitor <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
            ILogger <FhirDataStore> logger)
        {
            EnsureArg.IsNotNull(documentClient, nameof(documentClient));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(cosmosDocumentQueryFactory, nameof(cosmosDocumentQueryFactory));
            EnsureArg.IsNotNull(retryExceptionPolicyFactory, nameof(retryExceptionPolicyFactory));
            EnsureArg.IsNotNull(namedCosmosCollectionConfigurationAccessor, nameof(namedCosmosCollectionConfigurationAccessor));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _collectionConfiguration = namedCosmosCollectionConfigurationAccessor.Get(Constants.CollectionConfigurationName);

            _cosmosDocumentQueryFactory  = cosmosDocumentQueryFactory;
            _retryExceptionPolicyFactory = retryExceptionPolicyFactory;
            _logger         = logger;
            _documentClient = new FhirDocumentClient(documentClient.Value, fhirRequestContextAccessor, cosmosDataStoreConfiguration.ContinuationTokenSizeLimitInKb);
            _cosmosDataStoreConfiguration = cosmosDataStoreConfiguration;
            _collectionUri         = cosmosDataStoreConfiguration.GetRelativeCollectionUri(_collectionConfiguration.CollectionId);
            _upsertWithHistoryProc = new UpsertWithHistory();
            _hardDelete            = new HardDelete();
        }
        public CosmosContainerProvider(
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            IOptionsMonitor <CosmosCollectionConfiguration> collectionConfiguration,
            ICosmosClientInitializer cosmosClientInitializer,
            ILogger <CosmosContainerProvider> logger,
            IEnumerable <ICollectionInitializer> collectionInitializers)
        {
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(collectionConfiguration, nameof(collectionConfiguration));
            EnsureArg.IsNotNull(cosmosClientInitializer, nameof(cosmosClientInitializer));
            EnsureArg.IsNotNull(logger, nameof(logger));
            EnsureArg.IsNotNull(collectionInitializers, nameof(collectionInitializers));
            _logger = logger;

            string collectionId = collectionConfiguration.Get(Constants.CollectionConfigurationName).CollectionId;

            _client = cosmosClientInitializer.CreateCosmosClient(cosmosDataStoreConfiguration);

            _initializationOperation = new RetryableInitializationOperation(
                () => cosmosClientInitializer.InitializeDataStore(_client, cosmosDataStoreConfiguration, collectionInitializers));

            _container = new Lazy <Container>(() => cosmosClientInitializer.CreateFhirContainer(
                                                  _client,
                                                  cosmosDataStoreConfiguration.DatabaseId,
                                                  collectionId));
        }
Example #5
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CosmosFhirDataStore"/> class.
        /// </summary>
        /// <param name="containerScope">
        /// A function that returns an <see cref="Container"/>.
        /// Note that this is a function so that the lifetime of the instance is not directly controlled by the IoC container.
        /// </param>
        /// <param name="cosmosDataStoreConfiguration">The data store configuration.</param>
        /// <param name="namedCosmosCollectionConfigurationAccessor">The IOptions accessor to get a named version.</param>
        /// <param name="cosmosQueryFactory">The factory used to create the document query.</param>
        /// <param name="retryExceptionPolicyFactory">The retry exception policy factory.</param>
        /// <param name="logger">The logger instance.</param>
        /// <param name="modelInfoProvider">The model provider</param>
        /// <param name="coreFeatures">The core feature configuration</param>
        public CosmosFhirDataStore(
            IScoped <Container> containerScope,
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            IOptionsMonitor <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
            ICosmosQueryFactory cosmosQueryFactory,
            RetryExceptionPolicyFactory retryExceptionPolicyFactory,
            ILogger <CosmosFhirDataStore> logger,
            IModelInfoProvider modelInfoProvider,
            IOptions <CoreFeatureConfiguration> coreFeatures)
        {
            EnsureArg.IsNotNull(containerScope, nameof(containerScope));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(namedCosmosCollectionConfigurationAccessor, nameof(namedCosmosCollectionConfigurationAccessor));
            EnsureArg.IsNotNull(cosmosQueryFactory, nameof(cosmosQueryFactory));
            EnsureArg.IsNotNull(retryExceptionPolicyFactory, nameof(retryExceptionPolicyFactory));
            EnsureArg.IsNotNull(logger, nameof(logger));
            EnsureArg.IsNotNull(modelInfoProvider, nameof(modelInfoProvider));
            EnsureArg.IsNotNull(coreFeatures, nameof(coreFeatures));

            _containerScope              = containerScope;
            _cosmosQueryFactory          = cosmosQueryFactory;
            _retryExceptionPolicyFactory = retryExceptionPolicyFactory;
            _logger            = logger;
            _modelInfoProvider = modelInfoProvider;
            _coreFeatures      = coreFeatures.Value;
        }
Example #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="CosmosFhirDataStore"/> class.
        /// </summary>
        /// <param name="documentClientScope">
        /// A function that returns an <see cref="IDocumentClient"/>.
        /// Note that this is a function so that the lifetime of the instance is not directly controlled by the IoC container.
        /// </param>
        /// <param name="cosmosDataStoreConfiguration">The data store configuration.</param>
        /// <param name="namedCosmosCollectionConfigurationAccessor">The IOptions accessor to get a named version.</param>
        /// <param name="cosmosDocumentQueryFactory">The factory used to create the document query.</param>
        /// <param name="retryExceptionPolicyFactory">The retry exception policy factory.</param>
        /// <param name="logger">The logger instance.</param>
        /// <param name="modelInfoProvider">The model provider</param>
        public CosmosFhirDataStore(
            IScoped <IDocumentClient> documentClientScope,
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            IOptionsMonitor <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
            FhirCosmosDocumentQueryFactory cosmosDocumentQueryFactory,
            RetryExceptionPolicyFactory retryExceptionPolicyFactory,
            ILogger <CosmosFhirDataStore> logger,
            IModelInfoProvider modelInfoProvider)
        {
            EnsureArg.IsNotNull(documentClientScope, nameof(documentClientScope));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(namedCosmosCollectionConfigurationAccessor, nameof(namedCosmosCollectionConfigurationAccessor));
            EnsureArg.IsNotNull(cosmosDocumentQueryFactory, nameof(cosmosDocumentQueryFactory));
            EnsureArg.IsNotNull(retryExceptionPolicyFactory, nameof(retryExceptionPolicyFactory));
            EnsureArg.IsNotNull(logger, nameof(logger));
            EnsureArg.IsNotNull(modelInfoProvider, nameof(modelInfoProvider));

            _documentClientScope         = documentClientScope;
            _cosmosDocumentQueryFactory  = cosmosDocumentQueryFactory;
            _retryExceptionPolicyFactory = retryExceptionPolicyFactory;
            _logger            = logger;
            _modelInfoProvider = modelInfoProvider;

            CosmosCollectionConfiguration collectionConfiguration = namedCosmosCollectionConfigurationAccessor.Get(Constants.CollectionConfigurationName);

            DatabaseId    = cosmosDataStoreConfiguration.DatabaseId;
            CollectionId  = collectionConfiguration.CollectionId;
            CollectionUri = cosmosDataStoreConfiguration.GetRelativeCollectionUri(collectionConfiguration.CollectionId);

            _upsertWithHistoryProc = new UpsertWithHistory();
            _hardDelete            = new HardDelete();
        }
        /// <inheritdoc />
        public async Task InitializeDataStore(CosmosClient client, CosmosDataStoreConfiguration cosmosDataStoreConfiguration, IEnumerable <ICollectionInitializer> collectionInitializers)
        {
            EnsureArg.IsNotNull(client, nameof(client));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(collectionInitializers, nameof(collectionInitializers));

            try
            {
                _logger.LogInformation("Initializing Cosmos DB Database {DatabaseId} and collections", cosmosDataStoreConfiguration.DatabaseId);

                if (cosmosDataStoreConfiguration.AllowDatabaseCreation)
                {
                    _logger.LogInformation("CreateDatabaseIfNotExists {DatabaseId}", cosmosDataStoreConfiguration.DatabaseId);

                    await _retryExceptionPolicyFactory.GetRetryPolicy().ExecuteAsync(
                        async() =>
                        await client.CreateDatabaseIfNotExistsAsync(
                            cosmosDataStoreConfiguration.DatabaseId,
                            cosmosDataStoreConfiguration.InitialDatabaseThroughput.HasValue ? ThroughputProperties.CreateManualThroughput(cosmosDataStoreConfiguration.InitialDatabaseThroughput.Value) : null));
                }

                foreach (var collectionInitializer in collectionInitializers)
                {
                    await collectionInitializer.InitializeCollection(client);
                }

                _logger.LogInformation("Cosmos DB Database {DatabaseId} and collections successfully initialized", cosmosDataStoreConfiguration.DatabaseId);
            }
            catch (Exception ex)
            {
                LogLevel logLevel = ex is RequestRateExceededException ? LogLevel.Warning : LogLevel.Critical;
                _logger.Log(logLevel, ex, "Cosmos DB Database {DatabaseId} and collections initialization failed", cosmosDataStoreConfiguration.DatabaseId);
                throw;
            }
        }
Example #8
0
        /// <inheritdoc />
        public CosmosClient CreateCosmosClient(CosmosDataStoreConfiguration configuration)
        {
            EnsureArg.IsNotNull(configuration, nameof(configuration));

            var host = configuration.Host;
            var key  = configuration.Key;

            if (string.IsNullOrWhiteSpace(host) && string.IsNullOrWhiteSpace(key))
            {
                _logger.LogWarning("No connection string provided, attempting to connect to local emulator.");

                host = CosmosDbLocalEmulator.Host;
                key  = CosmosDbLocalEmulator.Key;
            }

            _logger.LogInformation("Creating CosmosClient instance for {DatabaseId}, Host: {Host}", configuration.DatabaseId, host);

            var builder = new CosmosClientBuilder(host, key)
                          .WithConnectionModeDirect(enableTcpConnectionEndpointRediscovery: true)
                          .WithCustomSerializer(new FhirCosmosSerializer())
                          .WithThrottlingRetryOptions(TimeSpan.FromSeconds(configuration.RetryOptions.MaxWaitTimeInSeconds), configuration.RetryOptions.MaxNumberOfRetries)
                          .AddCustomHandlers(_requestHandlers.ToArray());

            if (configuration.PreferredLocations?.Any() == true)
            {
                builder.WithApplicationPreferredRegions(configuration.PreferredLocations?.ToArray());
            }

            if (configuration.DefaultConsistencyLevel != null)
            {
                builder.WithConsistencyLevel(configuration.DefaultConsistencyLevel.Value);
            }

            return(builder.Build());
        }
Example #9
0
        public CollectionSettingsUpdater(ILogger <CollectionSettingsUpdater> logger, CosmosDataStoreConfiguration configuration)
        {
            EnsureArg.IsNotNull(logger, nameof(logger));
            EnsureArg.IsNotNull(configuration, nameof(configuration));

            _logger        = logger;
            _configuration = configuration;
        }
Example #10
0
        public RetryExceptionPolicyFactory(CosmosDataStoreConfiguration configuration)
        {
            EnsureArg.IsNotNull(configuration, nameof(configuration));

            _maxNumberOfRetries = configuration.RetryOptions.MaxNumberOfRetries;
            _minWaitTime        = TimeSpan.FromSeconds(Math.Min(RetryStrategy.DefaultMinBackoff.TotalSeconds, configuration.RetryOptions.MaxWaitTimeInSeconds));
            _maxWaitTime        = TimeSpan.FromSeconds(configuration.RetryOptions.MaxWaitTimeInSeconds);
        }
Example #11
0
        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);
        }
Example #12
0
        /// <inheritdoc />
        public IDocumentClient CreateDocumentClient(CosmosDataStoreConfiguration configuration)
        {
            EnsureArg.IsNotNull(configuration, nameof(configuration));

            _logger.LogInformation("Creating DocumentClient instance for {DatabaseUrl}", configuration.RelativeDatabaseUri);

            var connectionPolicy = new ConnectionPolicy
            {
                ConnectionMode     = configuration.ConnectionMode,
                ConnectionProtocol = configuration.ConnectionProtocol,
                RetryOptions       = new RetryOptions
                {
                    MaxRetryAttemptsOnThrottledRequests = configuration.RetryOptions.MaxNumberOfRetries,
                    MaxRetryWaitTimeInSeconds           = configuration.RetryOptions.MaxWaitTimeInSeconds,
                },
            };

            if (configuration.PreferredLocations != null && configuration.PreferredLocations.Any())
            {
                _logger.LogInformation("Setting DocumentClient PreferredLocations to {PreferredLocations}", string.Join(";", configuration.PreferredLocations));

                foreach (var preferredLocation in configuration.PreferredLocations)
                {
                    connectionPolicy.PreferredLocations.Add(preferredLocation);
                }
            }

            // Setting TypeNameHandling to any value other than 'None' will be flagged
            // as causing potential security issues
            var serializerSettings = new JsonSerializerSettings
            {
                NullValueHandling    = NullValueHandling.Ignore,
                DateFormatHandling   = DateFormatHandling.IsoDateFormat,
                DateParseHandling    = DateParseHandling.DateTimeOffset,
                DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
                TypeNameHandling     = TypeNameHandling.None,
            };

            serializerSettings.Converters.Add(new StringEnumConverter());

            // By default, the Json.NET serializer uses 'F' instead of 'f' for fractions.
            // 'F' will omit the trailing digits if they are 0. You might end up getting something like '2018-02-07T20:04:49.97114+00:00'
            // where the fraction is actually 971140. Because the ordering is done as string,
            // if the values don't always have complete 7 digits, the comparison might not work properly.
            serializerSettings.Converters.Add(new IsoDateTimeConverter {
                DateTimeFormat = "o"
            });

            return(new FhirDocumentClient(
                       new DocumentClient(new Uri(configuration.Host), configuration.Key, serializerSettings, connectionPolicy, configuration.DefaultConsistencyLevel),
                       _fhirRequestContextAccessor,
                       configuration.ContinuationTokenSizeLimitInKb,
                       _cosmosResponseProcessor));
        }
 public FhirCosmosResponseHandler(
     Func <IScoped <Container> > client,
     CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
     IFhirRequestContextAccessor fhirRequestContextAccessor,
     ICosmosResponseProcessor cosmosResponseProcessor)
 {
     _client = client;
     _cosmosDataStoreConfiguration = cosmosDataStoreConfiguration;
     _fhirRequestContextAccessor   = fhirRequestContextAccessor;
     _cosmosResponseProcessor      = cosmosResponseProcessor;
 }
        public CosmosDbSearchParameterStatusDataStore(
            Func <IScoped <Container> > containerScopeFactory,
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            ICosmosQueryFactory queryFactory)
        {
            EnsureArg.IsNotNull(containerScopeFactory, nameof(containerScopeFactory));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(queryFactory, nameof(queryFactory));

            _containerScopeFactory = containerScopeFactory;
            _queryFactory          = queryFactory;
        }
        public CosmosDbSearchParameterStatusInitializer(
            FilebasedSearchParameterStatusDataStore.Resolver filebasedSearchParameterStatusDataStoreResolver,
            ICosmosQueryFactory queryFactory,
            CosmosDataStoreConfiguration configuration)
        {
            EnsureArg.IsNotNull(filebasedSearchParameterStatusDataStoreResolver, nameof(filebasedSearchParameterStatusDataStoreResolver));
            EnsureArg.IsNotNull(queryFactory, nameof(queryFactory));

            _filebasedSearchParameterStatusDataStore = filebasedSearchParameterStatusDataStoreResolver.Invoke();
            _queryFactory  = queryFactory;
            _configuration = configuration;
        }
        public FhirCollectionSettingsUpdater(
            CosmosDataStoreConfiguration configuration,
            IOptionsMonitor <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
            ILogger <FhirCollectionSettingsUpdater> logger)
        {
            EnsureArg.IsNotNull(configuration, nameof(configuration));
            EnsureArg.IsNotNull(namedCosmosCollectionConfigurationAccessor, nameof(namedCosmosCollectionConfigurationAccessor));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _configuration           = configuration;
            _collectionConfiguration = namedCosmosCollectionConfigurationAccessor.Get(Constants.CollectionConfigurationName);
            _logger = logger;
        }
        public CollectionInitializer(string collectionId, CosmosDataStoreConfiguration cosmosDataStoreConfiguration, int?initialCollectionThroughput, IUpgradeManager upgradeManager, ILogger <CollectionInitializer> logger)
        {
            EnsureArg.IsNotNull(collectionId, nameof(collectionId));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(upgradeManager, nameof(upgradeManager));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _collectionId = collectionId;
            _cosmosDataStoreConfiguration = cosmosDataStoreConfiguration;
            _initialCollectionThroughput  = initialCollectionThroughput;
            _upgradeManager = upgradeManager;
            _logger         = logger;
        }
        /// <summary>
        /// Ensures that the necessary database and collection exist with the proper indexing policy and stored procedures
        /// </summary>
        /// <param name="documentClient">The <see cref="DocumentClient"/> instance to use for initialization.</param>
        /// <param name="cosmosDataStoreConfiguration">The data store configuration.</param>
        /// <returns>A task</returns>
        public async Task InitializeDataStore(IDocumentClient documentClient, CosmosDataStoreConfiguration cosmosDataStoreConfiguration)
        {
            EnsureArg.IsNotNull(documentClient, nameof(documentClient));
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));

            try
            {
                _logger.LogInformation("Initializing Cosmos DB collection {CollectionUri}", cosmosDataStoreConfiguration.AbsoluteCollectionUri);

                if (cosmosDataStoreConfiguration.AllowDatabaseCreation)
                {
                    _logger.LogDebug("CreateDatabaseIfNotExists {DatabaseId})", cosmosDataStoreConfiguration.DatabaseId);

                    await documentClient.CreateDatabaseIfNotExistsAsync(new Database
                    {
                        Id = cosmosDataStoreConfiguration.DatabaseId,
                    });
                }

                _logger.LogDebug("CreateDocumentCollectionIfNotExists {HostDescription}", cosmosDataStoreConfiguration.AbsoluteCollectionUri);

                DocumentCollection existingDocumentCollection = await documentClient.TryGetDocumentCollectionAsync(cosmosDataStoreConfiguration.RelativeCollectionUri);

                if (existingDocumentCollection == null)
                {
                    var documentCollection = new DocumentCollection
                    {
                        Id           = cosmosDataStoreConfiguration.CollectionId,
                        PartitionKey = new PartitionKeyDefinition
                        {
                            Paths =
                            {
                                $"/{KnownResourceWrapperProperties.PartitionKey}",
                            },
                        },
                    };

                    existingDocumentCollection = await documentClient.CreateDocumentCollectionIfNotExistsAsync(
                        cosmosDataStoreConfiguration.RelativeDatabaseUri, cosmosDataStoreConfiguration.RelativeCollectionUri, documentCollection);
                }

                await _upgradeManager.SetupCollectionAsync(documentClient, existingDocumentCollection);

                _logger.LogInformation("Cosmos DB collection {CollectionUri} successfully initialized", cosmosDataStoreConfiguration.AbsoluteCollectionUri);
            }
            catch (Exception ex)
            {
                _logger.LogCritical(ex, "Cosmos DB collection {CollectionUri} initialization failed", cosmosDataStoreConfiguration.AbsoluteCollectionUri);
                throw;
            }
        }
Example #19
0
        public RetryExceptionPolicyFactory(CosmosDataStoreConfiguration configuration, RequestContextAccessor <IFhirRequestContext> requestContextAccessor)
        {
            _requestContextAccessor = requestContextAccessor;
            EnsureArg.IsNotNull(configuration, nameof(configuration));
            EnsureArg.IsNotNull(requestContextAccessor, nameof(requestContextAccessor));

            _sdkOnlyRetryPolicy = Policy.NoOpAsync();

            _bundleActionRetryPolicy = configuration.IndividualBatchActionRetryOptions.MaxNumberOfRetries > 0
                ? CreateExtendedRetryPolicy(configuration.IndividualBatchActionRetryOptions.MaxNumberOfRetries / configuration.RetryOptions.MaxNumberOfRetries, configuration.IndividualBatchActionRetryOptions.MaxWaitTimeInSeconds)
                : Policy.NoOpAsync();

            _backgroundJobRetryPolicy = CreateExtendedRetryPolicy(100, -1);
        }
 public TestCosmosHealthCheck(
     IScoped <Container> container,
     CosmosDataStoreConfiguration configuration,
     IOptionsSnapshot <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
     ICosmosClientTestProvider testProvider,
     ILogger <CosmosHealthCheck> logger)
     : base(
         container,
         configuration,
         namedCosmosCollectionConfigurationAccessor,
         testProvider,
         logger)
 {
 }
        public DocumentClientProvider(
            CosmosDataStoreConfiguration cosmosDataStoreConfiguration,
            IDocumentClientInitializer documentClientInitializer,
            ILogger <DocumentClientProvider> logger)
        {
            EnsureArg.IsNotNull(cosmosDataStoreConfiguration, nameof(cosmosDataStoreConfiguration));
            EnsureArg.IsNotNull(documentClientInitializer, nameof(documentClientInitializer));
            EnsureArg.IsNotNull(logger, nameof(logger));

            _documentClient = documentClientInitializer.CreateDocumentClient(cosmosDataStoreConfiguration);

            _initializationOperation = new RetryableInitializationOperation(
                () => documentClientInitializer.InitializeDataStore(_documentClient, cosmosDataStoreConfiguration));
        }
Example #22
0
 public FhirCollectionUpgradeManager(
     IEnumerable <IFhirCollectionUpdater> collectionUpdater,
     CosmosDataStoreConfiguration configuration,
     IOptionsMonitor <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
     ICosmosDbDistributedLockFactory lockFactory,
     ILogger <FhirCollectionUpgradeManager> logger)
     : base(
         collectionUpdater,
         configuration,
         GetCosmosCollectionConfiguration(namedCosmosCollectionConfigurationAccessor, Constants.CollectionConfigurationName),
         lockFactory,
         logger)
 {
 }
Example #23
0
        public ControlPlaneDataStoreTests()
        {
            var scopedIDocumentClient = Substitute.For <IScoped <IDocumentClient> >();
            var documentClient        = Substitute.For <IDocumentClient>();

            scopedIDocumentClient.Value.Returns(documentClient);

            var cosmosDataStoreConfiguration = new CosmosDataStoreConfiguration
            {
                AllowDatabaseCreation = false,
                ConnectionMode        = ConnectionMode.Direct,
                ConnectionProtocol    = Protocol.Https,
                DatabaseId            = "testdatabaseid",
                Host = "https://fakehost",
                Key  = "ZmFrZWtleQ==",  // "fakekey"
                PreferredLocations = null,
            };

            var cosmosDocumentQueryFactory    = Substitute.For <ICosmosDocumentQueryFactory>();
            var identityProviderDocumentQuery = Substitute.For <IDocumentQuery <CosmosIdentityProvider> >();

            _cosmosIdentityProvider = Substitute.For <CosmosIdentityProvider>();
            _cosmosIdentityProvider.ETag.Returns("\"1\"");
            _cosmosIdentityProvider.Name.Returns("aad");
            _cosmosIdentityProvider.Audience.Returns(new[] { "fhir-api" });
            _cosmosIdentityProvider.Authority.Returns("https://login.microsoftonline.com/common");

            var feedResponse = new FeedResponse <dynamic>(new List <dynamic> {
                _cosmosIdentityProvider
            });

            identityProviderDocumentQuery.ExecuteNextAsync(Arg.Any <CancellationToken>()).Returns(feedResponse);
            cosmosDocumentQueryFactory.Create <CosmosIdentityProvider>(Arg.Any <IDocumentClient>(), Arg.Any <CosmosQueryContext>()).Returns(identityProviderDocumentQuery);

            var optionsMonitor = Substitute.For <IOptionsMonitor <CosmosCollectionConfiguration> >();

            optionsMonitor.Get(Constants.CollectionConfigurationName).Returns(new CosmosCollectionConfiguration {
                CollectionId = "collectionId"
            });

            var logger = NullLogger <ControlPlaneDataStore> .Instance;

            _controlPlaneDataStore = new ControlPlaneDataStore(
                scopedIDocumentClient,
                cosmosDataStoreConfiguration,
                cosmosDocumentQueryFactory,
                optionsMonitor,
                logger);
        }
Example #24
0
 public TestCosmosHealthCheck(
     IScoped <IDocumentClient> documentClient,
     CosmosDataStoreConfiguration configuration,
     IOptionsSnapshot <CosmosCollectionConfiguration> namedCosmosCollectionConfigurationAccessor,
     IDocumentClientTestProvider testProvider,
     ILogger <CosmosHealthCheck> logger)
     : base(
         documentClient,
         configuration,
         namedCosmosCollectionConfigurationAccessor,
         TestCosmosHealthCheckName,
         testProvider,
         logger)
 {
 }
        public FhirCosmosClientInitializerTests()
        {
            var clientTestProvider = Substitute.For <ICosmosClientTestProvider>();

            _cosmosDataStoreConfiguration = new CosmosDataStoreConfiguration();

            _initializer = new FhirCosmosClientInitializer(
                clientTestProvider,
                Enumerable.Empty <RequestHandler>(),
                NullLogger <FhirCosmosClientInitializer> .Instance);

            _collectionInitializers = new List <ICollectionInitializer> {
                _collectionInitializer1, _collectionInitializer2
            };
        }
Example #26
0
        public async Task PerformTest(Container container, CosmosDataStoreConfiguration configuration, CosmosCollectionConfiguration cosmosCollectionConfiguration)
        {
            var requestOptions = new ItemRequestOptions {
                ConsistencyLevel = ConsistencyLevel.Session
            };

            var resourceResponse = await container.UpsertItemAsync(
                _document,
                _partitionKey,
                requestOptions);

            requestOptions.SessionToken = resourceResponse.Headers.Session;

            await container.ReadItemAsync <HealthCheckDocument>(resourceResponse.Resource.Id, _partitionKey, requestOptions);
        }
        public async Task PerformTest(IDocumentClient documentClient, CosmosDataStoreConfiguration configuration, CosmosCollectionConfiguration cosmosCollectionConfiguration)
        {
            var requestOptions = new RequestOptions {
                ConsistencyLevel = ConsistencyLevel.Session, PartitionKey = _partitionKey
            };

            ResourceResponse <Document> resourceResponse = await documentClient.UpsertDocumentAsync(
                configuration.GetRelativeCollectionUri(cosmosCollectionConfiguration.CollectionId),
                _document,
                requestOptions);

            requestOptions.SessionToken = resourceResponse.SessionToken;

            await documentClient.ReadDocumentAsync(resourceResponse.Resource.SelfLink, requestOptions);
        }
        public DocumentClientInitializerTests()
        {
            _cosmosDataStoreConfiguration = new CosmosDataStoreConfiguration
            {
                AllowDatabaseCreation = false,
                ConnectionMode        = Azure.Documents.Client.ConnectionMode.Direct,
                ConnectionProtocol    = Azure.Documents.Client.Protocol.Https,
                DatabaseId            = "testdatabaseid",
                Host = "https://fakehost",
                Key  = "ZmFrZWtleQ==",  // "fakekey"
                PreferredLocations = null,
            };

            _cosmosDataStoreConfiguration.RetryOptions.MaxNumberOfRetries   = 10;
            _cosmosDataStoreConfiguration.RetryOptions.MaxWaitTimeInSeconds = 99;
        }
Example #29
0
        public FhirCosmosClientInitializerTests()
        {
            var clientTestProvider = Substitute.For <ICosmosClientTestProvider>();

            _cosmosDataStoreConfiguration = new CosmosDataStoreConfiguration();

            _initializer = new FhirCosmosClientInitializer(
                clientTestProvider,
                () => new[] { new TestRequestHandler() },
                new RetryExceptionPolicyFactory(_cosmosDataStoreConfiguration, Substitute.For <IFhirRequestContextAccessor>()),
                NullLogger <FhirCosmosClientInitializer> .Instance);

            _collectionInitializers = new List <ICollectionInitializer> {
                _collectionInitializer1, _collectionInitializer2
            };
        }
        public CosmosDbFhirStorageTestsFixture()
        {
            _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(),
            };
        }