public async Task <ExportJobOutcome> CreateExportJobAsync(ExportJobRecord jobRecord, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosExportJob = new CosmosExportJobRecordWrapper(jobRecord);

            try
            {
                ResourceResponse <Document> result = await _documentClient.CreateDocumentAsync(
                    _collectionUri,
                    cosmosExportJob,
                    new RequestOptions()
                {
                    PartitionKey = new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey)
                },
                    disableAutomaticIdGeneration : true,
                    cancellationToken : cancellationToken);

                return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(result.Resource.ETag)));
            }
            catch (DocumentClientException dce)
            {
                if (dce.StatusCode == HttpStatusCode.RequestEntityTooLarge)
                {
                    throw new RequestRateExceededException(dce.RetryAfter);
                }

                _logger.LogError(dce, "Unhandled Document Client Exception");
                throw;
            }
        }
        public async Task <ExportJobOutcome> CreateExportJobAsync(ExportJobRecord jobRecord, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosExportJob = new CosmosExportJobRecordWrapper(jobRecord);

            try
            {
                var result = await _containerScope.Value.CreateItemAsync(
                    cosmosExportJob,
                    new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey),
                    cancellationToken : cancellationToken);

                return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(result.Resource.ETag)));
            }
            catch (CosmosException dce)
            {
                if (dce.IsRequestRateExceeded())
                {
                    throw;
                }

                _logger.LogError(dce, "Failed to create an export job.");
                throw;
            }
        }
        public async Task <ExportJobOutcome> ReplaceExportJobAsync(ExportJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosExportJob = new CosmosExportJobRecordWrapper(jobRecord);

            var requestOptions = new RequestOptions()
            {
                PartitionKey = new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey),
            };

            // Create access condition so that record is replaced only if eTag matches.
            if (eTag != null)
            {
                requestOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = eTag.ToString(),
                };
            }

            try
            {
                ResourceResponse <Document> replaceResult = await _documentClient.ReplaceDocumentAsync(
                    UriFactory.CreateDocumentUri(_cosmosDataStoreConfiguration.DatabaseId, _collectionConfiguration.CollectionId, jobRecord.Id),
                    cosmosExportJob,
                    requestOptions,
                    cancellationToken : cancellationToken);

                var latestETag = replaceResult.Resource.ETag;
                return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(latestETag)));
            }
            catch (DocumentClientException dce)
            {
                if (dce.StatusCode == HttpStatusCode.RequestEntityTooLarge)
                {
                    throw new RequestRateExceededException(dce.RetryAfter);
                }

                if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
                {
                    throw new ResourceConflictException(eTag);
                }

                if (dce.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                }

                _logger.LogError(dce, "Unhandled Document Client Exception");
                throw;
            }
        }
        public async Task <ExportJobOutcome> UpdateExportJobAsync(ExportJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosExportJob = new CosmosExportJobRecordWrapper(jobRecord);

            var requestOptions = new RequestOptions()
            {
                PartitionKey = new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey),
            };

            // Create access condition so that record is replaced only if eTag matches.
            if (eTag != null)
            {
                requestOptions.AccessCondition = new AccessCondition()
                {
                    Type      = AccessConditionType.IfMatch,
                    Condition = eTag.VersionId,
                };
            }

            try
            {
                ResourceResponse <Document> replaceResult = await _retryExceptionPolicyFactory.CreateRetryPolicy().ExecuteAsync(
                    () => _documentClientScope.Value.ReplaceDocumentAsync(
                        UriFactory.CreateDocumentUri(DatabaseId, CollectionId, jobRecord.Id),
                        cosmosExportJob,
                        requestOptions,
                        cancellationToken));

                var latestETag = replaceResult.Resource.ETag;
                return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(latestETag)));
            }
            catch (DocumentClientException dce)
            {
                if (dce.StatusCode == HttpStatusCode.TooManyRequests)
                {
                    throw new RequestRateExceededException(dce.RetryAfter);
                }
                else if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
                {
                    throw new JobConflictException();
                }
                else if (dce.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                }

                _logger.LogError(dce, "Failed to update an export job.");
                throw;
            }
        }
        public async Task <ExportJobOutcome> UpdateExportJobAsync(ExportJobRecord jobRecord, WeakETag eTag, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(jobRecord, nameof(jobRecord));

            var cosmosExportJob = new CosmosExportJobRecordWrapper(jobRecord);
            var requestOptions  = new ItemRequestOptions();

            // Create access condition so that record is replaced only if eTag matches.
            if (eTag != null)
            {
                requestOptions.IfMatchEtag = eTag.VersionId;
            }

            try
            {
                var replaceResult = await _retryExceptionPolicyFactory.CreateRetryPolicy().ExecuteAsync(
                    () => _containerScope.Value.ReplaceItemAsync(
                        cosmosExportJob,
                        jobRecord.Id,
                        new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey),
                        cancellationToken: cancellationToken,
                        requestOptions: requestOptions));

                var latestETag = replaceResult.Resource.ETag;
                return(new ExportJobOutcome(jobRecord, WeakETag.FromVersionId(latestETag)));
            }
            catch (CosmosException dce)
            {
                if (dce.IsRequestRateExceeded())
                {
                    throw;
                }
                else if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
                {
                    throw new JobConflictException();
                }
                else if (dce.StatusCode == HttpStatusCode.NotFound)
                {
                    throw new JobNotFoundException(string.Format(Core.Resources.JobNotFound, jobRecord.Id));
                }

                _logger.LogError(dce, "Failed to update an export job.");
                throw;
            }
        }
        public async Task <ExportJobOutcome> GetExportJobByHashAsync(string hash, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNullOrWhiteSpace(hash, nameof(hash));

            try
            {
                IDocumentQuery <CosmosExportJobRecordWrapper> query = _documentClientScope.Value.CreateDocumentQuery <CosmosExportJobRecordWrapper>(
                    CollectionUri,
                    new SqlQuerySpec(
                        GetJobByHashQuery,
                        new SqlParameterCollection()
                {
                    new SqlParameter(HashParameterName, hash),
                }),
                    new FeedOptions {
                    PartitionKey = new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey)
                })
                                                                      .AsDocumentQuery();

                FeedResponse <CosmosExportJobRecordWrapper> result = await query.ExecuteNextAsync <CosmosExportJobRecordWrapper>();

                if (result.Count == 1)
                {
                    // We found an existing job that matches the hash.
                    CosmosExportJobRecordWrapper wrapper = result.First();

                    return(new ExportJobOutcome(wrapper.JobRecord, WeakETag.FromVersionId(wrapper.ETag)));
                }

                return(null);
            }
            catch (DocumentClientException dce)
            {
                if (dce.StatusCode == HttpStatusCode.TooManyRequests)
                {
                    throw new RequestRateExceededException(dce.RetryAfter);
                }

                _logger.LogError(dce, "Failed to get an export job by hash.");
                throw;
            }
        }
        public async Task <ExportJobOutcome> GetExportJobByHashAsync(string hash, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNullOrWhiteSpace(hash, nameof(hash));

            try
            {
                var query = _queryFactory.Create <CosmosExportJobRecordWrapper>(
                    _containerScope.Value,
                    new CosmosQueryContext(
                        new QueryDefinition(GetJobByHashQuery)
                        .WithParameter(HashParameterName, hash),
                        new QueryRequestOptions {
                    PartitionKey = new PartitionKey(CosmosDbExportConstants.ExportJobPartitionKey)
                }));

                FeedResponse <CosmosExportJobRecordWrapper> result = await query.ExecuteNextAsync();

                if (result.Count == 1)
                {
                    // We found an existing job that matches the hash.
                    CosmosExportJobRecordWrapper wrapper = result.First();

                    return(new ExportJobOutcome(wrapper.JobRecord, WeakETag.FromVersionId(wrapper.ETag)));
                }

                return(null);
            }
            catch (CosmosException dce)
            {
                if (dce.IsRequestRateExceeded())
                {
                    throw;
                }

                _logger.LogError(dce, "Failed to get an export job by hash.");
                throw;
            }
        }