diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusHandler.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusHandler.cs
new file mode 100644
index 0000000000..1b7e9f71a9
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusHandler.cs
@@ -0,0 +1,39 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Threading;
+using System.Threading.Tasks;
+using EnsureThat;
+using MediatR;
+
+namespace Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus
+{
+ ///
+ /// Handler for getting all async job statuses.
+ ///
+ public class GetAllJobStatusHandler : IRequestHandler
+ {
+ private readonly IJobStatusService _jobStatusService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The job status service.
+ public GetAllJobStatusHandler(IJobStatusService jobStatusService)
+ {
+ _jobStatusService = EnsureArg.IsNotNull(jobStatusService, nameof(jobStatusService));
+ }
+
+ ///
+ public async Task Handle(GetAllJobStatusRequest request, CancellationToken cancellationToken)
+ {
+ EnsureArg.IsNotNull(request, nameof(request));
+
+ var jobs = await _jobStatusService.GetAllJobStatusAsync(cancellationToken);
+
+ return new GetAllJobStatusResponse(jobs);
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusRequest.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusRequest.cs
new file mode 100644
index 0000000000..bd30888590
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusRequest.cs
@@ -0,0 +1,16 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using MediatR;
+
+namespace Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus
+{
+ ///
+ /// Request to get all async job statuses.
+ ///
+ public class GetAllJobStatusRequest : IRequest
+ {
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusResponse.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusResponse.cs
new file mode 100644
index 0000000000..f23e0dfa2f
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/GetAllJobStatusResponse.cs
@@ -0,0 +1,29 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+
+namespace Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus
+{
+ ///
+ /// Represents the response containing a list of job status information.
+ ///
+ public class GetAllJobStatusResponse
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The list of job status information.
+ public GetAllJobStatusResponse(IReadOnlyList jobs)
+ {
+ Jobs = jobs;
+ }
+
+ ///
+ /// Gets the list of job status information.
+ ///
+ public IReadOnlyList Jobs { get; }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/IJobStatusService.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/IJobStatusService.cs
new file mode 100644
index 0000000000..772e6d8f85
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/IJobStatusService.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus
+{
+ ///
+ /// Service interface for retrieving job status information.
+ ///
+ public interface IJobStatusService
+ {
+ ///
+ /// Gets the status information for all async jobs (Export, Import, Reindex, BulkDelete, BulkUpdate).
+ ///
+ /// The cancellation token.
+ /// A list of job status information.
+ Task> GetAllJobStatusAsync(CancellationToken cancellationToken);
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/JobStatusInfo.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/JobStatusInfo.cs
new file mode 100644
index 0000000000..891f0384ae
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/GetJobStatus/JobStatusInfo.cs
@@ -0,0 +1,51 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using Microsoft.Health.JobManagement;
+
+namespace Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus
+{
+ ///
+ /// Represents the status information for an async job.
+ ///
+ public class JobStatusInfo
+ {
+ ///
+ /// Gets or sets the group identifier for the job.
+ ///
+ public long GroupId { get; set; }
+
+ ///
+ /// Gets or sets the type of the job (e.g., Export, Import, Reindex).
+ ///
+ public string JobType { get; set; }
+
+ ///
+ /// Gets or sets the queue type.
+ ///
+ public QueueType QueueType { get; set; }
+
+ ///
+ /// Gets or sets the status of the job.
+ ///
+ public Microsoft.Health.JobManagement.JobStatus Status { get; set; }
+
+ ///
+ /// Gets or sets the content location URL for the job.
+ ///
+ public Uri ContentLocation { get; set; }
+
+ ///
+ /// Gets or sets the date and time when the job was created.
+ ///
+ public DateTime CreateDate { get; set; }
+
+ ///
+ /// Gets or sets the date and time when the job ended.
+ ///
+ public DateTime? EndDate { get; set; }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Operations/OperationsConstants.cs b/src/Microsoft.Health.Fhir.Core/Features/Operations/OperationsConstants.cs
index c6cf1db775..ed654f6982 100644
--- a/src/Microsoft.Health.Fhir.Core/Features/Operations/OperationsConstants.cs
+++ b/src/Microsoft.Health.Fhir.Core/Features/Operations/OperationsConstants.cs
@@ -59,6 +59,8 @@ public static class OperationsConstants
public const string ValueSetExpand = $"valueset-expand";
+ public const string Jobs = "jobs";
+
public static readonly ReadOnlyCollection ExcludedResourceTypesForBulkUpdate = new ReadOnlyCollection(new[] { "SearchParameter", "StructureDefinition" });
}
}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Routing/KnownRoutes.cs b/src/Microsoft.Health.Fhir.Core/Features/Routing/KnownRoutes.cs
index fc51a2e60c..dd3ccf3c7e 100644
--- a/src/Microsoft.Health.Fhir.Core/Features/Routing/KnownRoutes.cs
+++ b/src/Microsoft.Health.Fhir.Core/Features/Routing/KnownRoutes.cs
@@ -114,5 +114,7 @@ internal class KnownRoutes
public const string ExpandResourceType = KnownResourceTypes.ValueSet + "/" + Expand;
public const string ExpandResourceId = KnownResourceTypes.ValueSet + "/" + IdRouteSegment + "/" + Expand;
public const string ExpandOperationDefinition = OperationDefinition + "/" + OperationsConstants.ValueSetExpand;
+
+ public const string JobStatus = OperationsConstants.Operations + "/" + OperationsConstants.Jobs;
}
}
diff --git a/src/Microsoft.Health.Fhir.Core/Features/Routing/RouteNames.cs b/src/Microsoft.Health.Fhir.Core/Features/Routing/RouteNames.cs
index ea81baedd6..55c656b64c 100644
--- a/src/Microsoft.Health.Fhir.Core/Features/Routing/RouteNames.cs
+++ b/src/Microsoft.Health.Fhir.Core/Features/Routing/RouteNames.cs
@@ -100,5 +100,7 @@ internal static class RouteNames
internal const string ExpandById = nameof(ExpandById);
internal const string ExpandDefinition = nameof(ExpandDefinition);
+
+ internal const string GetAllJobStatus = nameof(GetAllJobStatus);
}
}
diff --git a/src/Microsoft.Health.Fhir.Core/Resources.Designer.cs b/src/Microsoft.Health.Fhir.Core/Resources.Designer.cs
index 533c6f8b8b..a00f024fb5 100644
--- a/src/Microsoft.Health.Fhir.Core/Resources.Designer.cs
+++ b/src/Microsoft.Health.Fhir.Core/Resources.Designer.cs
@@ -953,6 +953,15 @@ internal static string JobNotReindexOrchestratorJob {
}
}
+ ///
+ /// Looks up a localized string similar to The job status endpoint is not supported for Cosmos DB deployments..
+ ///
+ internal static string JobStatusNotSupportedForCosmosDb {
+ get {
+ return ResourceManager.GetString("JobStatusNotSupportedForCosmosDb", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Bundle.link values omitted because they exceeded the maximum Uri length..
///
@@ -1710,7 +1719,7 @@ internal static string SearchParameterDefinitionNullorEmptyCodeValue {
}
///
- /// Looks up a localized string similar to The search parameter with Uri '{0}' is defined by the FHIR specification and cannot be updated or deleted. Custom search parameters must use a different URL..
+ /// Looks up a localized string similar to The search parameter with Uri '{0}' is defined by the FHIR specification and cannot be updated or deleted. Custom search parameters must use a different URL..
///
internal static string SearchParameterDefinitionSystemDefined {
get {
diff --git a/src/Microsoft.Health.Fhir.Core/Resources.resx b/src/Microsoft.Health.Fhir.Core/Resources.resx
index 64bac96ba3..df303a3f2b 100644
--- a/src/Microsoft.Health.Fhir.Core/Resources.resx
+++ b/src/Microsoft.Health.Fhir.Core/Resources.resx
@@ -870,4 +870,8 @@
There are no search parameters to reindex for job Id: {0}.
-
\ No newline at end of file
+
+ The job status endpoint is not supported for Cosmos DB deployments.
+ Error message when job status endpoint is called on a Cosmos DB deployment
+
+
diff --git a/src/Microsoft.Health.Fhir.CosmosDb/Features/Operations/CosmosJobStatusService.cs b/src/Microsoft.Health.Fhir.CosmosDb/Features/Operations/CosmosJobStatusService.cs
new file mode 100644
index 0000000000..57339ef045
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.CosmosDb/Features/Operations/CosmosJobStatusService.cs
@@ -0,0 +1,26 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus;
+
+namespace Microsoft.Health.Fhir.CosmosDb.Features.Operations
+{
+ ///
+ /// Cosmos DB implementation of the job status service.
+ /// This feature is not supported on Cosmos DB.
+ ///
+ public class CosmosJobStatusService : IJobStatusService
+ {
+ ///
+ public Task> GetAllJobStatusAsync(CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException(Fhir.Core.Resources.JobStatusNotSupportedForCosmosDb);
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Controllers/JobStatusController.cs b/src/Microsoft.Health.Fhir.Shared.Api/Controllers/JobStatusController.cs
new file mode 100644
index 0000000000..738f15c874
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Shared.Api/Controllers/JobStatusController.cs
@@ -0,0 +1,54 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Threading.Tasks;
+using EnsureThat;
+using MediatR;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Health.Api.Features.Audit;
+using Microsoft.Health.Fhir.Api.Features.ActionResults;
+using Microsoft.Health.Fhir.Api.Features.Filters;
+using Microsoft.Health.Fhir.Api.Features.Resources;
+using Microsoft.Health.Fhir.Api.Features.Routing;
+using Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus;
+using Microsoft.Health.Fhir.Core.Features.Routing;
+using Microsoft.Health.Fhir.ValueSets;
+
+namespace Microsoft.Health.Fhir.Api.Controllers
+{
+ ///
+ /// Controller for retrieving job status information.
+ ///
+ [ServiceFilter(typeof(AuditLoggingFilterAttribute))]
+ [ServiceFilter(typeof(OperationOutcomeExceptionFilterAttribute))]
+ [ValidateModelState]
+ public class JobStatusController : Controller
+ {
+ private readonly IMediator _mediator;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The mediator.
+ public JobStatusController(IMediator mediator)
+ {
+ _mediator = EnsureArg.IsNotNull(mediator, nameof(mediator));
+ }
+
+ ///
+ /// Gets the status of all async jobs (Export, Import, Reindex, BulkDelete, BulkUpdate).
+ ///
+ /// A list of job status information.
+ [HttpGet]
+ [Route(KnownRoutes.JobStatus, Name = RouteNames.GetAllJobStatus)]
+ [AuditEventType(AuditEventSubType.Read)]
+ public async Task GetAllJobStatus()
+ {
+ var response = await _mediator.Send(new GetAllJobStatusRequest(), HttpContext.RequestAborted);
+
+ return FhirResult.Create(response.Jobs.ToJobStatusResult());
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/JobStatusResponseExtensions.cs b/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/JobStatusResponseExtensions.cs
new file mode 100644
index 0000000000..7f09ca08dc
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.Shared.Api/Features/Resources/JobStatusResponseExtensions.cs
@@ -0,0 +1,81 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System.Collections.Generic;
+using System.Linq;
+using Hl7.Fhir.Model;
+using Microsoft.Health.Fhir.Core.Extensions;
+using Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus;
+using Microsoft.Health.Fhir.Core.Models;
+
+namespace Microsoft.Health.Fhir.Api.Features.Resources
+{
+ ///
+ /// Extension methods for job status responses.
+ ///
+ public static class JobStatusResponseExtensions
+ {
+ ///
+ /// Converts a list of JobStatusInfo to a FHIR Parameters resource.
+ ///
+ /// The list of job status information.
+ /// A FHIR Parameters resource as a ResourceElement
+ public static ResourceElement ToJobStatusResult(this IReadOnlyList jobs)
+ {
+ var parameters = new Parameters();
+
+ foreach (var job in jobs)
+ {
+ var part = new Parameters.ParameterComponent
+ {
+ Name = job.JobType + " " + job.GroupId,
+ };
+
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "id",
+ Value = new Integer64(job.GroupId),
+ });
+
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "type",
+ Value = new FhirString(job.JobType),
+ });
+
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "uri",
+ Value = new FhirUri(job.ContentLocation),
+ });
+
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "status",
+ Value = new FhirString(job.Status.ToString()),
+ });
+
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "createTime",
+ Value = new FhirDateTime(job.CreateDate),
+ });
+
+ if (job.EndDate != null)
+ {
+ part.Part.Add(new Parameters.ParameterComponent
+ {
+ Name = "endTime",
+ Value = new FhirDateTime((System.DateTimeOffset)job.EndDate),
+ });
+ }
+
+ parameters.Parameter.Add(part);
+ }
+
+ return parameters.ToResourceElement();
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.Shared.Api/Microsoft.Health.Fhir.Shared.Api.projitems b/src/Microsoft.Health.Fhir.Shared.Api/Microsoft.Health.Fhir.Shared.Api.projitems
index f6bf0cd8ea..11f02c5e0e 100644
--- a/src/Microsoft.Health.Fhir.Shared.Api/Microsoft.Health.Fhir.Shared.Api.projitems
+++ b/src/Microsoft.Health.Fhir.Shared.Api/Microsoft.Health.Fhir.Shared.Api.projitems
@@ -16,6 +16,7 @@
+
@@ -49,6 +50,7 @@
+
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Features/Operations/SqlJobStatusService.cs b/src/Microsoft.Health.Fhir.SqlServer/Features/Operations/SqlJobStatusService.cs
new file mode 100644
index 0000000000..09073c1f72
--- /dev/null
+++ b/src/Microsoft.Health.Fhir.SqlServer/Features/Operations/SqlJobStatusService.cs
@@ -0,0 +1,134 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Threading;
+using System.Threading.Tasks;
+using EnsureThat;
+using Microsoft.Data.SqlClient;
+using Microsoft.Extensions.Logging;
+using Microsoft.Health.Fhir.Core.Features.Operations;
+using Microsoft.Health.Fhir.Core.Features.Operations.GetJobStatus;
+using Microsoft.Health.Fhir.Core.Features.Routing;
+using Microsoft.Health.JobManagement;
+using Microsoft.Health.SqlServer.Features.Client;
+
+namespace Microsoft.Health.Fhir.SqlServer.Features.Operations
+{
+ ///
+ /// SQL Server implementation of the job status service.
+ ///
+ public class SqlJobStatusService : IJobStatusService
+ {
+ private readonly SqlConnectionWrapperFactory _sqlConnectionWrapperFactory;
+ private readonly IUrlResolver _urlResolver;
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The SQL connection wrapper factory.
+ /// The URL resolver.
+ /// The logger.
+ public SqlJobStatusService(
+ SqlConnectionWrapperFactory sqlConnectionWrapperFactory,
+ IUrlResolver urlResolver,
+ ILogger logger)
+ {
+ _sqlConnectionWrapperFactory = EnsureArg.IsNotNull(sqlConnectionWrapperFactory, nameof(sqlConnectionWrapperFactory));
+ _urlResolver = EnsureArg.IsNotNull(urlResolver, nameof(urlResolver));
+ _logger = EnsureArg.IsNotNull(logger, nameof(logger));
+ }
+
+ ///
+ public async Task> GetAllJobStatusAsync(CancellationToken cancellationToken)
+ {
+ var jobs = new List();
+
+ var queueTypes = new[]
+ {
+ QueueType.Export,
+ QueueType.Import,
+ QueueType.Reindex,
+ QueueType.BulkDelete,
+ QueueType.BulkUpdate,
+ };
+
+ using SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true);
+ using SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateRetrySqlCommand();
+
+ foreach (var queueType in queueTypes)
+ {
+ var queueJobs = await GetJobsByQueueTypeAsync(sqlCommandWrapper, queueType, cancellationToken);
+ jobs.AddRange(queueJobs);
+ }
+
+ return jobs;
+ }
+
+ private async Task> GetJobsByQueueTypeAsync(SqlCommandWrapper sqlCommandWrapper, QueueType queueType, CancellationToken cancellationToken)
+ {
+ var jobs = new List();
+ var operationName = GetOperationNameForQueueType(queueType);
+
+ sqlCommandWrapper.CommandText = @"
+ SELECT GroupId, min(Status) AS Status, min(CreateDate) AS CreateDate, max(EndDate) AS EndDate
+ FROM dbo.JobQueue
+ WHERE QueueType = @QueueType
+ AND Status <> 5
+ GROUP BY GroupId
+ ORDER BY CreateDate DESC";
+
+ sqlCommandWrapper.Parameters.Clear();
+ sqlCommandWrapper.Parameters.AddWithValue("@QueueType", (byte)queueType);
+
+ try
+ {
+ using SqlDataReader reader = await sqlCommandWrapper.ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);
+ while (await reader.ReadAsync(cancellationToken))
+ {
+ var groupId = reader.GetInt64(0);
+ var status = (JobStatus)reader.GetByte(1);
+ var createDate = reader.GetDateTime(2);
+ DateTime? endDate = await reader.IsDBNullAsync(5, cancellationToken) ? null : reader.GetDateTime(3);
+
+ var jobStatusInfo = new JobStatusInfo
+ {
+ GroupId = groupId,
+ QueueType = queueType,
+ JobType = operationName,
+ Status = status,
+ ContentLocation = _urlResolver.ResolveOperationResultUrl(operationName, groupId.ToString()),
+ CreateDate = createDate,
+ EndDate = endDate,
+ };
+
+ jobs.Add(jobStatusInfo);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Error retrieving jobs for queue type {QueueType}", queueType);
+ }
+
+ return jobs;
+ }
+
+ private static string GetOperationNameForQueueType(QueueType queueType)
+ {
+ return queueType switch
+ {
+ QueueType.Export => OperationsConstants.Export,
+ QueueType.Import => OperationsConstants.Import,
+ QueueType.Reindex => OperationsConstants.Reindex,
+ QueueType.BulkDelete => OperationsConstants.BulkDelete,
+ QueueType.BulkUpdate => OperationsConstants.BulkUpdate,
+ _ => queueType.ToString(),
+ };
+ }
+ }
+}
diff --git a/src/Microsoft.Health.Fhir.SqlServer/Registration/FhirServerBuilderSqlServerRegistrationExtensions.cs b/src/Microsoft.Health.Fhir.SqlServer/Registration/FhirServerBuilderSqlServerRegistrationExtensions.cs
index 66d43333bb..2d08a5ba47 100644
--- a/src/Microsoft.Health.Fhir.SqlServer/Registration/FhirServerBuilderSqlServerRegistrationExtensions.cs
+++ b/src/Microsoft.Health.Fhir.SqlServer/Registration/FhirServerBuilderSqlServerRegistrationExtensions.cs
@@ -86,6 +86,11 @@ public static IFhirServerBuilder AddSqlServer(this IFhirServerBuilder fhirServer
.AsSelf()
.AsImplementedInterfaces();
+ services.Add()
+ .Scoped()
+ .AsSelf()
+ .AsImplementedInterfaces();
+
services.Add()
.Singleton()
.AsImplementedInterfaces();