And one moment I use API and we have API-Key authentication.
public class AssessmentJobsService : DomainService, IAssessmentJobsService
{
#region Fields
private readonly IAssessmentDetailRepository _assessmentDetailRepository;
private readonly IMediaRepository _mediaRepository;
private readonly IBlobManager _blobManager;
private readonly IAssessmentRepository _assessmentRepository;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="AssessmentJobsService" /> class.
/// </summary>
/// <param name="assessmentDetailRepository">The assessment repository.</param>
/// <param name="mediaRepository">The media repository.</param>
/// <param name="blobManager">The blob manager.</param>
public AssessmentJobsService(
IAssessmentDetailRepository assessmentDetailRepository,
IMediaRepository mediaRepository,
IBlobManager blobManager,
IAssessmentRepository assessmentRepository)
{
_assessmentDetailRepository = assessmentDetailRepository;
_mediaRepository = mediaRepository;
_blobManager = blobManager;
_assessmentRepository = assessmentRepository;
}
#endregion
/// <inheritdoc/>
public void SheduleAssessmentCleanup(long assessmentId)
{
BackgroundJob.Schedule(() => CleanUpAssessment(assessmentId), TimeSpan.FromSeconds(30));
}
public void SheduleEmptyAssessmentCleanup(long assessmentId)
{
BackgroundJob.Schedule(() => CleanUpEmptyAssessment(assessmentId), TimeSpan.FromHours(4));
}
/// <inheritdoc/>
public void SheduleMediaCleanup(long mediaId)
{
BackgroundJob.Schedule(() => CleanUpMedia(mediaId), TimeSpan.FromHours(4));
}
/// <inheritdoc/>
public async Task StartTrackingJob(long assessmentDetailId, bool isOneStepProcess)
{
// Create a Hangfire job to finish the job if not all methods are called within 5 minutes
var jobId = BackgroundJob.Schedule(() => CheckProcessVideoTracking(assessmentDetailId, isOneStepProcess), TimeSpan.FromMinutes(3));
var assessementDetails = await _assessmentDetailRepository.GetAsync(assessmentDetailId);
assessementDetails.JobId = jobId;
// Sets default status
assessementDetails.ProcessVideoTrackStatus = (int)ProcessVideoTrackingStatus.InProgress;
assessementDetails.ProcessVideoTrackStatusValue = ProcessVideoTrackingStatus.InProgress.ToString();
await _assessmentDetailRepository.UpdateAsync(assessementDetails, true);
}
/// <inheritdoc/>
//[AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 300, 600, 900, 1800, 3600 })]
[AutomaticRetry(Attempts = 5, DelaysInSeconds = new[] { 60, 60, 60 })]
public async Task CheckProcessVideoTracking(long assessmentDetailId, bool isOneStepProcess)
{
var assessmentDetails = await _assessmentDetailRepository.GetAsync(assessmentDetailId)
?? throw new Exception($"Job failed. Assessment details were not found");
var status = (ProcessVideoTrackingStatus)assessmentDetails.ProcessVideoTrackStatus;
if ((isOneStepProcess && status == ProcessVideoTrackingStatus.ProcessData) ||
(!isOneStepProcess && status == (ProcessVideoTrackingStatus.ProcessData | ProcessVideoTrackingStatus.Report | ProcessVideoTrackingStatus.Overlay)))
{
return;
}
throw new Exception($"Job failed. Status {status}");
}
/// <inheritdoc/>
public async Task CleanUpMedia(long mediaId)
{
var mediaIsInUse = await _mediaRepository.CheckMediaWithAssessmentDetails(mediaId);
if (!mediaIsInUse)
{
var media = await _mediaRepository.FindAsync(mediaId);
if(media == null)
{
return;
}
await _mediaRepository.DeleteAsync(mediaId);
await _blobManager.DeleteBlob(media?.MediaUrl, media?.FileName);
}
}
/// <inheritdoc/>
public async Task CleanUpMediaMedBridge()
{
try
{
// Get details from assessment where externalAssessmentId != null
var mediaIds = await _assessmentDetailRepository.GetListMediaIdAndCleanDetailsMedbridgeAsync();
if (mediaIds != null)
{
var mediaQueryable = await _mediaRepository.GetQueryableAsync();
var medias = mediaQueryable.Where(m => mediaIds.Contains(m.Id)).ToList();
if (medias == null)
{
return;
}
await _mediaRepository.DeleteManyAsync(mediaIds, true);
foreach (var media in medias)
{
await _blobManager.DeleteBlob(media?.MediaUrl, media?.FileName);
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
/// <inheritdoc/>
public async Task CleanUpAssessment(long assessmentId)
{
var details = await _assessmentDetailRepository.GetListAsync(x => x.AssessmentId == assessmentId);
var mediaIds = details.Select(x => x.MediaId).ToList();
await _assessmentDetailRepository.DeleteManyAsync(details);
var medias = await _mediaRepository.GetListAsync(x => mediaIds.Contains(x.Id));
await _mediaRepository.DeleteManyAsync(medias);
foreach (var media in medias)
{
await _blobManager.DeleteBlob(media.MediaUrl, media.FileName);
}
}
public async Task CleanUpEmptyAssessment(long assessmentId)
{
if (!await _assessmentDetailRepository.CheckAssessmentWithAssessmentDetails(assessmentId))
{
var assessment = await _assessmentRepository.FindAsync(assessmentId);
if (assessment != null)
{
await _assessmentRepository.DeleteAsync(assessment);
}
}
}
}
and in HttpHostModule
private void ConfigureHangfire(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddHangfire(config =>
{
GlobalConfiguration.Configuration.UsePostgreSqlStorage(configuration.GetConnectionString("Default"));
});
}
We are using MultiTenancy. When I created scheduled job and it was executed it doesn't work as expected. It doesn't have the correct context - TenantId is null. But when I was calling job Tenat was not null. Does it mean that hangfire jobs doesn't work with multiTenancy. Or if you can suggest some approach please write.
Is it enough for stack trace. Or we need to add this code and we will have more information?
13:40:27 INF] Executed endpoint 'Health checks' [13:40:27 INF] Event dropped by processor CustomSentryProcessor [13:40:27 INF] Request finished HTTP/1.1 GET http://10.244.6.75:80/health-status - - - 200 - application/json 4.1482ms [13:40:27 INF] Executed endpoint 'Health checks' [13:40:27 INF] Event dropped by processor CustomSentryProcessor [13:40:27 INF] Request finished HTTP/1.1 GET http://10.244.6.75:80/health-status - - - 200 - application/json 3.0396ms [13:40:27 ERR] ---------- RemoteServiceErrorInfo ---------- { "code": null, "message": "An internal error occurred during your request!", "details": null, "data": {}, "validationErrors": null }
[13:40:27 ERR] Undefined feature: risk
Volo.Abp.AbpException: Undefined feature: risk
at Volo.Abp.Features.FeatureDefinitionManager.GetAsync(String name)
at Volo.Abp.FeatureManagement.FeatureManager.GetOrNullInternalAsync(String name, String providerName, String providerKey, Boolean fallback)
at Volo.Abp.FeatureManagement.FeatureManager.GetOrNullWithProviderAsync(String name, String providerName, String providerKey, Boolean fallback)
at Volo.Abp.FeatureManagement.FeatureAppService.GetAsync(String providerName, String providerKey)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Authorization.AuthorizationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue
1.ProceedAsync()
at Volo.Abp.GlobalFeatures.GlobalFeatureInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func
3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Auditing.AuditingInterceptor.ProceedByLoggingAsync(IAbpMethodInvocation invocation, AbpAuditingOptions options, IAuditingHelper auditingHelper, IAuditLogScope auditLogScope) at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue
1.ProceedAsync()
at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func
3 proceed)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter
1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at lambda_method3149(Closure, Object) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask
1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
[13:40:27 INF] Executing ObjectResult, writing value of type 'Volo.Abp.Http.RemoteServiceErrorResponse'.
[13:40:27 INF] Executed action Volo.Abp.FeatureManagement.FeaturesController.GetAsync (Volo.Abp.FeatureManagement.HttpApi) in 251.6018ms
[13:40:27 INF] Executed endpoint 'Volo.Abp.FeatureManagement.FeaturesController.GetAsync (Volo.Abp.FeatureManagement.HttpApi)'
[13:40:27 INF] Envelope queued up: 'd86a37c4329848568ba1fa8efc199355'
[13:40:27 INF] Request finished HTTP/1.1 GET http://api-dev.3motionai.com/api/feature-management/features?providerName=T - - - 500 - application/json;+charset=utf-8 274.8530ms
[13:40:27 INF] Envelope 'd86a37c4329848568ba1fa8efc199355' successfully sent.
[13:40:34 INF] Start processing HTTP request GET http://3m-backend-api-webhost-dev2-55cd8c9cd4-qkxzs/health-status
[13:40:34 INF] Sending HTTP request GET http://3m-backend-api-webhost-dev2-55cd8c9cd4-qkxzs/health-status
[13:40:34 INF] Request starting HTTP/1.1 GET http://3m-backend-api-webhost-dev2-55cd8c9cd4-qkxzs/health-status - -
[13:40:34 INF] Started transaction with span ID 'f1cd941c0dff465c' and trace ID '86a44028d26f496a91756b61b7026d02'.
[13:40:34 INF] Executing endpoint 'Health checks'
[13:40:34 INF] Executed endpoint 'Health checks'
[13:40:34 INF] Received HTTP response headers after 3.8044ms - 200
[13:40:34 INF] End processing HTTP request after 3.9389ms - 200
[13:40:34 INF] Event dropped by processor CustomSentryProcessor
[13:40:34 INF] Request finished HTTP/1.1 GET http://3m-backend-api-webhost-dev2-55cd8c9cd4-qkxzs/health-status - - - 200 - application/json 3.7909ms
[13:40:42 INF] Request starting HTTP/1.1 GET http://10.244.6.75:80/health-status - -
[13:40:42 INF] Request starting HTTP/1.1 GET http://10.244.6.75:80/health-status - -
[13:40:42 INF] Sentry trace header is null. Creating new Sentry Propagation Context.
[13:40:42 INF] Sentry trace header is null. Creating new Sentry Propagation Context.
[13:40:42 INF] Started transaction with span ID 'e3d7716683cd4290' and trace ID '46a9a0a8ae604a9fa92fa0bda97e34b8'.
[13:40:42 INF] Started transaction with span ID '57415cff88434c49' and trace ID '92d5b63d8cbe40d79d0872310c55d61e'.
[13:40:42 INF] Executing endpoint 'Health checks'
I can send You stackTrace
[13:39:47 INF] Request starting HTTP/1.1 GET http://api-dev.3motionai.com/api/app/assessment-types/movements - -
[13:39:47 INF] Sentry trace header is null. Creating new Sentry Propagation Context.
[13:39:47 INF] Started transaction with span ID '3407bb84c47c4acd' and trace ID '18271856ff714539a8b97737c3b2da46'.
[13:39:47 INF] Executing endpoint '_3M.AssessmentTypes.AssessmentTypesAppService.GetMovements (_3M.Application)'
[13:39:47 INF] Route matched with {action = "GetMovements", controller = "AssessmentTypes", area = "", page = ""}. Executing controller action with signature System.Threading.Tasks.Task1[System.Collections.Generic.List
1[_3M.AssessmentTypes.AssessmentTypeDto]] GetMovements() on controller _3M.AssessmentTypes.AssessmentTypesAppService (_3M.Application).
[13:39:48 ERR] ---------- RemoteServiceErrorInfo ----------
{
"code": null,
"message": "An internal error occurred during your request!",
"details": null,
"data": {},
"validationErrors": null
}
[13:39:48 ERR] Undefined feature: Sports
Volo.Abp.AbpException: Undefined feature: Sports
at Volo.Abp.Features.FeatureDefinitionManager.GetAsync(String name)
at Volo.Abp.Features.FeatureChecker.GetOrNullAsync(String name)
at Volo.Abp.Features.FeatureCheckerBase.IsEnabledAsync(String name)
at _3M.AssessmentTypes.AssessmentTypesAppService.GetListAssessmentTypesWithCheckedFeatures(List1 asmtypes) in /azp/_work/1/s/src/_3M.Application/AssessmentTypes/AssessmentTypesAppService.cs:line 80 at _3M.AssessmentTypes.AssessmentTypesAppService.GetMovements() in /azp/_work/1/s/src/_3M.Application/AssessmentTypes/AssessmentTypesAppService.cs:line 58 at lambda_method2576(Closure, Object) at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask
1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
[13:39:48 INF] Executing ObjectResult, writing value of type 'Volo.Abp.Http.RemoteServiceErrorResponse'.
[13:39:48 INF] Executed action _3M.AssessmentTypes.AssessmentTypesAppService.GetMovements (_3M.Application) in 450.3411ms
[13:39:48 INF] Executed endpoint '_3M.AssessmentTypes.AssessmentTypesAppService.GetMovements (_3M.Application)'
[13:39:48 INF] Enve
This part x => await _featureChecker.IsEnabledAsync(x.Code) will not work it needs WhenAll but this works with repository and I had some problems with WhenAll and asynchronous calls to db.
My public class AssessmentFeatureDefinitionProvider : FeatureDefinitionProvider { private readonly IAssessmentTypeRepository _assessmentTypeRepository; private const string FeatureNameForSingleMovements = "Tasks"; private const string FeatureNameForProtocols = "Protocols";
public AssessmentFeatureDefinitionProvider(IAssessmentTypeRepository assessmentTypeRepository)
{
_assessmentTypeRepository = assessmentTypeRepository;
}
public override async void Define(IFeatureDefinitionContext context)
{
var tasksGroup = context.AddGroup(FeatureNameForSingleMovements);
var protocolGroup = context.AddGroup(FeatureNameForProtocols);
await DefineFeaturesForAssessmentType(tasksGroup, AssessmentTypeNames.Movement);
await DefineFeaturesForAssessmentType(protocolGroup, AssessmentTypeNames.Protocol);
}
private async Task DefineFeaturesForAssessmentType(FeatureGroupDefinition contextGroup, AssessmentTypeNames assessmentTypeName)
{
var assessmentTypes = await _assessmentTypeRepository.GetAssessmentTypes(assessmentTypeName);
foreach (var assessmentType in assessmentTypes)
{
var feature = contextGroup.AddFeature(assessmentType.Code, defaultValue: "true", displayName: L($"DisplayName:AssessmentType.{assessmentType.Code}"));
var movements = assessmentType.AssessmentTypeMovements.Select(x => x.AssessmentMovement).ToList();
foreach (var movement in movements)
{
feature.CreateChild(movement.Code, defaultValue: "true", displayName: L($"DisplayName:AssessmentMovement.{movement.Code}"));
}
}
}
private static LocalizableString L(string name)
{
return LocalizableString.Create<_3MFeatureResource>(name);
}
}
And is it ok that I have FeatureDefinitionProvider in the domain project
I will try. But what about get features it fell down two. And can You answer me can feature has a name with white spaces?