public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
                request.DestinationInfo,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            if (outcome == null)
            {
                var jobRecord = new ExportJobRecord(request.RequestUri, hash, requestorClaims);

                // Store the destination secret.
                await _secretStore.SetSecretAsync(jobRecord.SecretName, request.DestinationInfo.ToJson());

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }
        public async Task <CreateExportResponse> Handle(CreateExportRequest request, CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(request, nameof(request));

            IReadOnlyCollection <KeyValuePair <string, string> > requestorClaims = _claimsExtractor.Extract()?
                                                                                   .OrderBy(claim => claim.Key, StringComparer.Ordinal).ToList();

            // Compute the hash of the job.
            var hashObject = new
            {
                request.RequestUri,
                RequestorClaims = requestorClaims,
                request.DestinationInfo,
            };

            string hash = JsonConvert.SerializeObject(hashObject).ComputeHash();

            // Check to see if a matching job exists or not. If a matching job exists, we will return that instead.
            // Otherwise, we will create a new export job. This will be a best effort since the likelihood of this happen should be small.
            ExportJobOutcome outcome = await _fhirOperationDataStore.GetExportJobByHashAsync(hash, cancellationToken);

            if (outcome == null)
            {
                // Remove the connection settings from the request URI before we store it in the secret store.
                NameValueCollection queryParameters = HttpUtility.ParseQueryString(request.RequestUri.Query);

                queryParameters.Remove(KnownQueryParameterNames.DestinationType);
                queryParameters.Remove(KnownQueryParameterNames.DestinationConnectionSettings);

                var uriBuilder = new UriBuilder(request.RequestUri);
                uriBuilder.Query = queryParameters.ToString();

                var jobRecord = new ExportJobRecord(uriBuilder.Uri, request.ResourceType, hash, requestorClaims);

                // Store the destination secret.
                try
                {
                    await _secretStore.SetSecretAsync(jobRecord.SecretName, request.DestinationInfo.ToJson(), cancellationToken);
                }
                catch (SecretStoreException sse)
                {
                    throw new OperationFailedException(string.Format(Resources.OperationFailed, OperationsConstants.Export, sse.Message), sse.ResponseStatusCode);
                }

                outcome = await _fhirOperationDataStore.CreateExportJobAsync(jobRecord, cancellationToken);
            }

            return(new CreateExportResponse(outcome.JobRecord.Id));
        }
        public async Task GivenSetSecretFails_WhenCreatingAnExportJob_ThenThrowsOperationFailedException()
        {
            // Set up create export request handler with mock secret store.
            ISecretStore   mockSecretStore = Substitute.For <ISecretStore>();
            HttpStatusCode errorStatusCode = HttpStatusCode.InternalServerError;

            mockSecretStore.SetSecretAsync(Arg.Any <string>(), Arg.Any <string>(), Arg.Any <CancellationToken>())
            .Returns <SecretWrapper>(_ => throw new SecretStoreException(SecretStoreErrors.SetSecretError, innerException: null, statusCode: errorStatusCode));

            _createExportRequestHandler = new CreateExportRequestHandler(_claimsExtractor, _fhirOperationDataStore, mockSecretStore);

            var request = new CreateExportRequest(RequestUrl, DestinationType, ConnectionString);

            OperationFailedException ofe = await Assert.ThrowsAsync <OperationFailedException>(() => _createExportRequestHandler.Handle(request, _cancellationToken));

            Assert.NotNull(ofe);
            Assert.Equal(errorStatusCode, ofe.ResponseStatusCode);
        }