feat: Implement workflow and form management endpoints
- Added endpoints for managing form definitions including: - GetFormDefinitionById - GetFormDefinitionList - PublishFormDefinition - SubmitFormData - UpdateFormDefinition - Added endpoints for managing workflow definitions including: - CreateWorkflowDefinition - DeleteWorkflowDefinition - DisableWorkflowDefinition - GetWorkflowDefinitionById - GetWorkflowDefinitionList - PublishWorkflowDefinition - UpdateWorkflowDefinition - Added endpoints for managing workflow instances including: - GetWorkflowInstanceById - GetWorkflowInstanceList - MonitorWorkflowInstances - ResumeWorkflowInstance - StartWorkflowInstance - SuspendWorkflowInstance - WithdrawWorkflowInstance - Added endpoints for managing workflow tasks including: - ApproveTask - DelegateTask - GetCcTasks - GetHistoryTasks - GetOverdueTasks - GetPendingTasks - GetTaskById - RejectTask - TransferTask - UrgeTask - Introduced middleware for handling API responses and global exceptions. - Configured application settings for database connection and JWT authentication.
This commit is contained in:
parent
f26a20a875
commit
f49e0ea1e4
17
src/Workflow.Api/Configuration/JwtAuthConfiguration.cs
Normal file
17
src/Workflow.Api/Configuration/JwtAuthConfiguration.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using FastEndpoints.Security;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Workflow.Api.Configuration;
|
||||
|
||||
public static class JwtAuthConfiguration
|
||||
{
|
||||
public static void AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var jwtConfig = configuration.GetSection("Jwt");
|
||||
|
||||
services.AddJWTBearerAuth(jwtConfig["SigningKey"]!, bearerEvents: null);
|
||||
|
||||
services.AddAuthorizationBuilder();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.DTOs;
|
||||
using Workflow.Application.Form.FormDefinition.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class CreateFormDefinitionEndpoint : Endpoint<CreateFormDefinitionRequest, FormDefinitionDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public CreateFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/forms");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Create a new form definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(CreateFormDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new CreateFormDefinitionCommand(req.Name, req.Code, req.Description, req.Fields);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateFormDefinitionRequest
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public List<FormFieldDto> Fields { get; set; } = new();
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.FormDefinition.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class DeleteFormDefinitionEndpoint : Endpoint<DeleteFormDefinitionRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Delete("/forms/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Delete a form definition (soft delete)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(DeleteFormDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new DeleteFormDefinitionCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class DeleteFormDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.DTOs;
|
||||
using Workflow.Application.Form.FormData.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class GetFormDataByInstanceEndpoint : Endpoint<GetFormDataByInstanceRequest, FormDataDto?>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetFormDataByInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/forms/data/{InstanceId}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get form data by workflow instance id";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetFormDataByInstanceRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetFormDataByInstanceQuery(req.InstanceId);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetFormDataByInstanceRequest
|
||||
{
|
||||
public Guid InstanceId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.DTOs;
|
||||
using Workflow.Application.Form.FormDefinition.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class GetFormDefinitionByIdEndpoint : Endpoint<GetFormDefinitionByIdRequest, FormDefinitionDetailDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetFormDefinitionByIdEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/forms/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get form definition detail by id (includes fields)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetFormDefinitionByIdRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetFormDefinitionByIdQuery(req.Id);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetFormDefinitionByIdRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.DTOs;
|
||||
using Workflow.Application.Form.FormDefinition.Queries;
|
||||
using Workflow.Domain.Enums;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class GetFormDefinitionListEndpoint : Endpoint<GetFormDefinitionListRequest, PagedResult<FormDefinitionDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetFormDefinitionListEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/forms");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get paginated list of form definitions";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetFormDefinitionListRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetFormDefinitionListQuery(req.PageIndex, req.PageSize, req.Status);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetFormDefinitionListRequest
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public FormStatus? Status { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.FormDefinition.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class PublishFormDefinitionEndpoint : Endpoint<PublishFormDefinitionRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public PublishFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/forms/{Id}/publish");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Publish a form definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(PublishFormDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new PublishFormDefinitionCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class PublishFormDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
36
src/Workflow.Api/Endpoints/Form/SubmitFormDataEndpoint.cs
Normal file
36
src/Workflow.Api/Endpoints/Form/SubmitFormDataEndpoint.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.FormData.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class SubmitFormDataEndpoint : Endpoint<SubmitFormDataRequest, Guid>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public SubmitFormDataEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/forms/data");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Submit form data for a workflow instance";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(SubmitFormDataRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new SubmitFormDataCommand(req.FormDefinitionId, req.InstanceId, req.DataJson);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class SubmitFormDataRequest
|
||||
{
|
||||
public Guid FormDefinitionId { get; set; }
|
||||
public Guid InstanceId { get; set; }
|
||||
public string DataJson { get; set; } = string.Empty;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Form.DTOs;
|
||||
using Workflow.Application.Form.FormDefinition.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.Form;
|
||||
|
||||
public class UpdateFormDefinitionEndpoint : Endpoint<UpdateFormDefinitionRequest, FormDefinitionDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public UpdateFormDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Put("/forms/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Update a form definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UpdateFormDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new UpdateFormDefinitionCommand(req.Id, req.Name, req.Description, req.Fields);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateFormDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public List<FormFieldDto> Fields { get; set; } = new();
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.DTOs;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class CreateWorkflowDefinitionEndpoint : Endpoint<CreateWorkflowDefinitionRequest, WorkflowDefinitionDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public CreateWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-definitions");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Create a new workflow definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(CreateWorkflowDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new CreateWorkflowDefinitionCommand(req.Name, req.Code, req.Description);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateWorkflowDefinitionRequest
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class DeleteWorkflowDefinitionEndpoint : Endpoint<DeleteWorkflowDefinitionRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DeleteWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Delete("/workflow-definitions/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Delete a workflow definition (soft delete)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(DeleteWorkflowDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new DeleteWorkflowDefinitionCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class DeleteWorkflowDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class DisableWorkflowDefinitionEndpoint : Endpoint<DisableWorkflowDefinitionRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DisableWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-definitions/{Id}/disable");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Disable a workflow definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(DisableWorkflowDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new DisableWorkflowDefinitionCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class DisableWorkflowDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.DTOs;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class GetWorkflowDefinitionByIdEndpoint : Endpoint<GetWorkflowDefinitionByIdRequest, WorkflowDefinitionDetailDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetWorkflowDefinitionByIdEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-definitions/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get workflow definition detail by id (includes nodes and edges)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetWorkflowDefinitionByIdRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetWorkflowDefinitionByIdQuery(req.Id);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetWorkflowDefinitionByIdRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.DTOs;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Queries;
|
||||
using Workflow.Domain.Enums;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class GetWorkflowDefinitionListEndpoint : Endpoint<GetWorkflowDefinitionListRequest, PagedResult<WorkflowDefinitionDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetWorkflowDefinitionListEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-definitions");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get paginated list of workflow definitions";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetWorkflowDefinitionListRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetWorkflowDefinitionListQuery(req.PageIndex, req.PageSize, req.Status);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetWorkflowDefinitionListRequest
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public DefinitionStatus? Status { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class PublishWorkflowDefinitionEndpoint : Endpoint<PublishWorkflowDefinitionRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public PublishWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-definitions/{Id}/publish");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Publish a workflow definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(PublishWorkflowDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new PublishWorkflowDefinitionCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class PublishWorkflowDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.Commands;
|
||||
using Workflow.Application.Features.WorkflowDefinitions.DTOs;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowDefinition;
|
||||
|
||||
public class UpdateWorkflowDefinitionEndpoint : Endpoint<UpdateWorkflowDefinitionRequest, WorkflowDefinitionDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public UpdateWorkflowDefinitionEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Put("/workflow-definitions/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Update a workflow definition";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UpdateWorkflowDefinitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new UpdateWorkflowDefinitionCommand(req.Id, req.Name, req.Description, req.DefinitionJson);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class UpdateWorkflowDefinitionRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string? Description { get; set; }
|
||||
public string? DefinitionJson { get; set; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.DTOs;
|
||||
using Workflow.Application.Features.WorkflowInstances.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class GetWorkflowInstanceByIdEndpoint : Endpoint<GetWorkflowInstanceByIdRequest, WorkflowInstanceDetailDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetWorkflowInstanceByIdEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-instances/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get workflow instance detail by id (includes tokens and tasks)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetWorkflowInstanceByIdRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetWorkflowInstanceByIdQuery(req.Id);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetWorkflowInstanceByIdRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowInstances.DTOs;
|
||||
using Workflow.Application.Features.WorkflowInstances.Queries;
|
||||
using Workflow.Domain.Enums;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class GetWorkflowInstanceListEndpoint : Endpoint<GetWorkflowInstanceListRequest, PagedResult<WorkflowInstanceDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetWorkflowInstanceListEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-instances");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get paginated list of workflow instances";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetWorkflowInstanceListRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetWorkflowInstanceListQuery(req.PageIndex, req.PageSize, req.Status, req.InitiatorId);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetWorkflowInstanceListRequest
|
||||
{
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
public InstanceStatus? Status { get; set; }
|
||||
public Guid? InitiatorId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.DTOs;
|
||||
using Workflow.Application.Features.WorkflowInstances.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class MonitorWorkflowInstancesEndpoint : EndpointWithoutRequest<WorkflowMonitorDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public MonitorWorkflowInstancesEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-instances/monitor");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get workflow monitoring statistics";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(CancellationToken ct)
|
||||
{
|
||||
var query = new MonitorWorkflowInstancesQuery();
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class ResumeWorkflowInstanceEndpoint : Endpoint<ResumeWorkflowInstanceRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public ResumeWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-instances/{Id}/resume");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Resume a suspended workflow instance";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(ResumeWorkflowInstanceRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new ResumeWorkflowInstanceCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class ResumeWorkflowInstanceRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class StartWorkflowInstanceEndpoint : Endpoint<StartWorkflowInstanceRequest, Guid>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public StartWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-instances");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Start a new workflow instance";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(StartWorkflowInstanceRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new StartWorkflowInstanceCommand(req.DefinitionCode, req.Title, req.Variables);
|
||||
var result = await _mediator.Send(command, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class StartWorkflowInstanceRequest
|
||||
{
|
||||
public string DefinitionCode { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string? Variables { get; set; }
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class SuspendWorkflowInstanceEndpoint : Endpoint<SuspendWorkflowInstanceRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public SuspendWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-instances/{Id}/suspend");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Suspend a running workflow instance";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(SuspendWorkflowInstanceRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new SuspendWorkflowInstanceCommand(req.Id);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class SuspendWorkflowInstanceRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowInstances.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowInstance;
|
||||
|
||||
public class WithdrawWorkflowInstanceEndpoint : Endpoint<WithdrawWorkflowInstanceRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public WithdrawWorkflowInstanceEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-instances/{Id}/withdraw");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Withdraw a workflow instance (initiator only)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(WithdrawWorkflowInstanceRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new WithdrawWorkflowInstanceCommand(req.Id, req.UserId);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class WithdrawWorkflowInstanceRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class ApproveTaskEndpoint : Endpoint<ApproveTaskRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public ApproveTaskEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-tasks/{Id}/approve");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Approve a pending task";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(ApproveTaskRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new ApproveTaskCommand(req.Id, req.UserId, req.Comment);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class ApproveTaskRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class DelegateTaskEndpoint : Endpoint<DelegateTaskRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public DelegateTaskEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-tasks/{Id}/delegate");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Delegate a task to another user";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(DelegateTaskRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new DelegateTaskCommand(req.Id, req.FromUserId, req.ToUserId);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class DelegateTaskRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid FromUserId { get; set; }
|
||||
public Guid ToUserId { get; set; }
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class GetCcTasksEndpoint : Endpoint<GetCcTasksRequest, PagedResult<WorkflowTaskListItemDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetCcTasksEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-tasks/cc");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get CC (carbon copy) tasks for a user";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetCcTasksRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetCcTasksQuery(req.UserId, req.PageIndex, req.PageSize);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetCcTasksRequest
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class GetHistoryTasksEndpoint : Endpoint<GetHistoryTasksRequest, PagedResult<WorkflowTaskListItemDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetHistoryTasksEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-tasks/history");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get completed (approved/rejected) tasks for a user";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetHistoryTasksRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetHistoryTasksQuery(req.UserId, req.PageIndex, req.PageSize);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetHistoryTasksRequest
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class GetOverdueTasksEndpoint : Endpoint<GetOverdueTasksRequest, PagedResult<WorkflowTaskListItemDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetOverdueTasksEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-tasks/overdue");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get overdue tasks (past due date, still pending)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetOverdueTasksRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetOverdueTasksQuery(req.UserId, req.PageIndex, req.PageSize);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetOverdueTasksRequest
|
||||
{
|
||||
public Guid? UserId { get; set; }
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Common;
|
||||
using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class GetPendingTasksEndpoint : Endpoint<GetPendingTasksRequest, PagedResult<WorkflowTaskListItemDto>>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetPendingTasksEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-tasks/pending");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get pending tasks for a user";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetPendingTasksRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetPendingTasksQuery(req.UserId, req.PageIndex, req.PageSize);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetPendingTasksRequest
|
||||
{
|
||||
public Guid UserId { get; set; }
|
||||
public int PageIndex { get; set; } = 1;
|
||||
public int PageSize { get; set; } = 20;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class GetTaskByIdEndpoint : Endpoint<GetTaskByIdRequest, WorkflowTaskListItemDto>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public GetTaskByIdEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/workflow-tasks/{Id}");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Get task detail by id";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetTaskByIdRequest req, CancellationToken ct)
|
||||
{
|
||||
var query = new GetTaskByIdQuery(req.Id);
|
||||
var result = await _mediator.Send(query, ct);
|
||||
await SendAsync(result, 200, ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class GetTaskByIdRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class RejectTaskEndpoint : Endpoint<RejectTaskRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public RejectTaskEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-tasks/{Id}/reject");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Reject a pending task";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(RejectTaskRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new RejectTaskCommand(req.Id, req.UserId, req.Comment);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class RejectTaskRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string? Comment { get; set; }
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class TransferTaskEndpoint : Endpoint<TransferTaskRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public TransferTaskEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-tasks/{Id}/transfer");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Transfer a task to another user";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(TransferTaskRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new TransferTaskCommand(req.Id, req.FromUserId, req.ToUserId);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class TransferTaskRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid FromUserId { get; set; }
|
||||
public Guid ToUserId { get; set; }
|
||||
}
|
||||
35
src/Workflow.Api/Endpoints/WorkflowTask/UrgeTaskEndpoint.cs
Normal file
35
src/Workflow.Api/Endpoints/WorkflowTask/UrgeTaskEndpoint.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using FastEndpoints;
|
||||
using MediatR;
|
||||
using Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
namespace Workflow.Api.Endpoints.WorkflowTask;
|
||||
|
||||
public class UrgeTaskEndpoint : Endpoint<UrgeTaskRequest>
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public UrgeTaskEndpoint(IMediator mediator) => _mediator = mediator;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/workflow-tasks/{Id}/urge");
|
||||
AllowAnonymous();
|
||||
Summary(s =>
|
||||
{
|
||||
s.Summary = "Urge a pending task (send notification to assignee)";
|
||||
});
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(UrgeTaskRequest req, CancellationToken ct)
|
||||
{
|
||||
var command = new UrgeTaskCommand(req.Id, req.UserId);
|
||||
await _mediator.Send(command, ct);
|
||||
await SendOkAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
public class UrgeTaskRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
}
|
||||
71
src/Workflow.Api/Middleware/ApiResponseMiddleware.cs
Normal file
71
src/Workflow.Api/Middleware/ApiResponseMiddleware.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Workflow.Api.Middleware;
|
||||
|
||||
public class ApiResponseMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public ApiResponseMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
var originalBodyStream = context.Response.Body;
|
||||
|
||||
using var responseBody = new MemoryStream();
|
||||
context.Response.Body = responseBody;
|
||||
|
||||
await _next(context);
|
||||
|
||||
context.Response.Body = originalBodyStream;
|
||||
|
||||
responseBody.Seek(0, SeekOrigin.Begin);
|
||||
var responseText = await new StreamReader(responseBody).ReadToEndAsync();
|
||||
|
||||
// Skip wrapping for non-JSON responses, error responses, or Swagger
|
||||
if (context.Response.StatusCode >= 400 ||
|
||||
context.Response.ContentType?.Contains("application/json") != true ||
|
||||
context.Request.Path.StartsWithSegments("/swagger"))
|
||||
{
|
||||
responseBody.Seek(0, SeekOrigin.Begin);
|
||||
await responseBody.CopyToAsync(originalBodyStream);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the original response and wrap it in the standard envelope
|
||||
object? data;
|
||||
if (string.IsNullOrWhiteSpace(responseText))
|
||||
{
|
||||
data = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
data = JsonSerializer.Deserialize<JsonElement>(responseText);
|
||||
}
|
||||
catch
|
||||
{
|
||||
data = responseText;
|
||||
}
|
||||
}
|
||||
|
||||
var wrappedResponse = new
|
||||
{
|
||||
code = 0,
|
||||
data,
|
||||
message = "ok"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(wrappedResponse, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
|
||||
context.Response.ContentLength = System.Text.Encoding.UTF8.GetByteCount(json);
|
||||
await context.Response.WriteAsync(json);
|
||||
}
|
||||
}
|
||||
67
src/Workflow.Api/Middleware/GlobalExceptionMiddleware.cs
Normal file
67
src/Workflow.Api/Middleware/GlobalExceptionMiddleware.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Workflow.Domain.Exceptions;
|
||||
|
||||
namespace Workflow.Api.Middleware;
|
||||
|
||||
public class GlobalExceptionMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<GlobalExceptionMiddleware> _logger;
|
||||
|
||||
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _next(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HandleExceptionAsync(context, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||
{
|
||||
var (statusCode, message) = exception switch
|
||||
{
|
||||
BusinessException ex => ((int)HttpStatusCode.BadRequest, ex.Message),
|
||||
NotFoundException ex => ((int)HttpStatusCode.NotFound, ex.Message),
|
||||
UnauthorizedException ex => ((int)HttpStatusCode.Unauthorized, ex.Message),
|
||||
InvalidStateTransitionException ex => ((int)HttpStatusCode.BadRequest, ex.Message),
|
||||
_ => ((int)HttpStatusCode.InternalServerError, "An unexpected error occurred.")
|
||||
};
|
||||
|
||||
if (statusCode == (int)HttpStatusCode.InternalServerError)
|
||||
{
|
||||
_logger.LogError(exception, "Unhandled exception: {Message}", exception.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(exception, "Business exception: {Message}", exception.Message);
|
||||
}
|
||||
|
||||
context.Response.StatusCode = statusCode;
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
var response = new
|
||||
{
|
||||
code = statusCode,
|
||||
message,
|
||||
data = (object?)null
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(response, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
});
|
||||
|
||||
await context.Response.WriteAsync(json);
|
||||
}
|
||||
}
|
||||
59
src/Workflow.Api/Program.cs
Normal file
59
src/Workflow.Api/Program.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System.Text.Json;
|
||||
using FastEndpoints;
|
||||
using FastEndpoints.Swagger;
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Workflow.Api.Configuration;
|
||||
using Workflow.Api.Middleware;
|
||||
using Workflow.Application.Form.FormDefinition.Commands;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// DbContext
|
||||
builder.Services.AddDbContext<WorkflowDbContext>(options =>
|
||||
{
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("Default"), npgsql =>
|
||||
{
|
||||
npgsql.MigrationsAssembly(typeof(WorkflowDbContext).Assembly.FullName);
|
||||
});
|
||||
});
|
||||
|
||||
// MediatR
|
||||
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(CreateFormDefinitionCommand).Assembly));
|
||||
|
||||
// FastEndpoints
|
||||
builder.Services.AddFastEndpoints();
|
||||
builder.Services.SwaggerDocument();
|
||||
|
||||
// JWT Authentication
|
||||
builder.Services.AddJwtAuthentication(builder.Configuration);
|
||||
|
||||
// CORS
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddDefaultPolicy(policy =>
|
||||
{
|
||||
policy.WithOrigins("http://localhost:5173", "http://localhost:5174", "http://localhost:3000", "http://localhost:5666")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials();
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Middleware pipeline (order matters)
|
||||
app.UseCors();
|
||||
app.UseMiddleware<GlobalExceptionMiddleware>();
|
||||
app.UseMiddleware<ApiResponseMiddleware>();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseFastEndpoints(config =>
|
||||
{
|
||||
config.Endpoints.RoutePrefix = "api";
|
||||
config.Serializer.Options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
});
|
||||
app.UseSwaggerGen();
|
||||
|
||||
app.Run();
|
||||
@ -46,24 +46,208 @@ TemporaryDependencyNodeTargetIdentifier=net10.0
|
||||
/optimize-
|
||||
/out:obj/Debug/net10.0/Workflow.Api.dll
|
||||
/refout:obj/Debug/net10.0/refint/Workflow.Api.dll
|
||||
/target:library
|
||||
/target:exe
|
||||
/warnaserror+
|
||||
/utf8output
|
||||
/deterministic+
|
||||
/langversion:14.0
|
||||
/features:use-roslyn-tokenizer=true
|
||||
/warnaserror+:NU1605,SYSLIB0011
|
||||
|
||||
[sourceFiles]
|
||||
Configuration/JwtAuthConfiguration.cs
|
||||
Endpoints/Form/
|
||||
CreateFormDefinitionEndpoint.cs
|
||||
DeleteFormDefinitionEndpoint.cs
|
||||
GetFormDataByInstanceEndpoint.cs
|
||||
GetFormDefinitionByIdEndpoint.cs
|
||||
GetFormDefinitionListEndpoint.cs
|
||||
PublishFormDefinitionEndpoint.cs
|
||||
SubmitFormDataEndpoint.cs
|
||||
UpdateFormDefinitionEndpoint.cs
|
||||
Endpoints/WorkflowDefinition/
|
||||
CreateWorkflowDefinitionEndpoint.cs
|
||||
DeleteWorkflowDefinitionEndpoint.cs
|
||||
DisableWorkflowDefinitionEndpoint.cs
|
||||
GetWorkflowDefinitionByIdEndpoint.cs
|
||||
GetWorkflowDefinitionListEndpoint.cs
|
||||
PublishWorkflowDefinitionEndpoint.cs
|
||||
UpdateWorkflowDefinitionEndpoint.cs
|
||||
Endpoints/WorkflowInstance/
|
||||
GetWorkflowInstanceByIdEndpoint.cs
|
||||
GetWorkflowInstanceListEndpoint.cs
|
||||
MonitorWorkflowInstancesEndpoint.cs
|
||||
ResumeWorkflowInstanceEndpoint.cs
|
||||
StartWorkflowInstanceEndpoint.cs
|
||||
SuspendWorkflowInstanceEndpoint.cs
|
||||
WithdrawWorkflowInstanceEndpoint.cs
|
||||
Endpoints/WorkflowTask/
|
||||
ApproveTaskEndpoint.cs
|
||||
DelegateTaskEndpoint.cs
|
||||
GetCcTasksEndpoint.cs
|
||||
GetHistoryTasksEndpoint.cs
|
||||
GetOverdueTasksEndpoint.cs
|
||||
GetPendingTasksEndpoint.cs
|
||||
GetTaskByIdEndpoint.cs
|
||||
RejectTaskEndpoint.cs
|
||||
TransferTaskEndpoint.cs
|
||||
UrgeTaskEndpoint.cs
|
||||
Middleware/
|
||||
ApiResponseMiddleware.cs
|
||||
GlobalExceptionMiddleware.cs
|
||||
obj/Debug/net10.0/
|
||||
.NETCoreApp,Version=v10.0.AssemblyAttributes.cs
|
||||
SwaggerExportPathInitializer.g.cs
|
||||
Workflow.Api.AssemblyInfo.cs
|
||||
Workflow.Api.GlobalUsings.g.cs
|
||||
Program.cs
|
||||
|
||||
[metadataReferences]
|
||||
../
|
||||
Workflow.Application/obj/Debug/net10.0/ref/Workflow.Application.dll
|
||||
Workflow.Domain/obj/Debug/net10.0/ref/Workflow.Domain.dll
|
||||
Workflow.Infrastructure/obj/Debug/net10.0/ref/Workflow.Infrastructure.dll
|
||||
<DOTNET>/packs/Microsoft.AspNetCore.App.Ref/10.0.7/ref/net10.0/
|
||||
Microsoft.AspNetCore.Antiforgery.dll
|
||||
Microsoft.AspNetCore.Authentication.Abstractions.dll
|
||||
Microsoft.AspNetCore.Authentication.BearerToken.dll
|
||||
Microsoft.AspNetCore.Authentication.Cookies.dll
|
||||
Microsoft.AspNetCore.Authentication.Core.dll
|
||||
Microsoft.AspNetCore.Authentication.dll
|
||||
Microsoft.AspNetCore.Authentication.OAuth.dll
|
||||
Microsoft.AspNetCore.Authorization.dll
|
||||
Microsoft.AspNetCore.Authorization.Policy.dll
|
||||
Microsoft.AspNetCore.Components.Authorization.dll
|
||||
Microsoft.AspNetCore.Components.dll
|
||||
Microsoft.AspNetCore.Components.Endpoints.dll
|
||||
Microsoft.AspNetCore.Components.Forms.dll
|
||||
Microsoft.AspNetCore.Components.Server.dll
|
||||
Microsoft.AspNetCore.Components.Web.dll
|
||||
Microsoft.AspNetCore.Connections.Abstractions.dll
|
||||
Microsoft.AspNetCore.CookiePolicy.dll
|
||||
Microsoft.AspNetCore.Cors.dll
|
||||
Microsoft.AspNetCore.Cryptography.Internal.dll
|
||||
Microsoft.AspNetCore.Cryptography.KeyDerivation.dll
|
||||
Microsoft.AspNetCore.DataProtection.Abstractions.dll
|
||||
Microsoft.AspNetCore.DataProtection.dll
|
||||
Microsoft.AspNetCore.DataProtection.Extensions.dll
|
||||
Microsoft.AspNetCore.Diagnostics.Abstractions.dll
|
||||
Microsoft.AspNetCore.Diagnostics.dll
|
||||
Microsoft.AspNetCore.Diagnostics.HealthChecks.dll
|
||||
Microsoft.AspNetCore.dll
|
||||
Microsoft.AspNetCore.HostFiltering.dll
|
||||
Microsoft.AspNetCore.Hosting.Abstractions.dll
|
||||
Microsoft.AspNetCore.Hosting.dll
|
||||
Microsoft.AspNetCore.Hosting.Server.Abstractions.dll
|
||||
Microsoft.AspNetCore.Html.Abstractions.dll
|
||||
Microsoft.AspNetCore.Http.Abstractions.dll
|
||||
Microsoft.AspNetCore.Http.Connections.Common.dll
|
||||
Microsoft.AspNetCore.Http.Connections.dll
|
||||
Microsoft.AspNetCore.Http.dll
|
||||
Microsoft.AspNetCore.Http.Extensions.dll
|
||||
Microsoft.AspNetCore.Http.Features.dll
|
||||
Microsoft.AspNetCore.Http.Results.dll
|
||||
Microsoft.AspNetCore.HttpLogging.dll
|
||||
Microsoft.AspNetCore.HttpOverrides.dll
|
||||
Microsoft.AspNetCore.HttpsPolicy.dll
|
||||
Microsoft.AspNetCore.Identity.dll
|
||||
Microsoft.AspNetCore.Localization.dll
|
||||
Microsoft.AspNetCore.Localization.Routing.dll
|
||||
Microsoft.AspNetCore.Metadata.dll
|
||||
Microsoft.AspNetCore.Mvc.Abstractions.dll
|
||||
Microsoft.AspNetCore.Mvc.ApiExplorer.dll
|
||||
Microsoft.AspNetCore.Mvc.Core.dll
|
||||
Microsoft.AspNetCore.Mvc.Cors.dll
|
||||
Microsoft.AspNetCore.Mvc.DataAnnotations.dll
|
||||
Microsoft.AspNetCore.Mvc.dll
|
||||
Microsoft.AspNetCore.Mvc.Formatters.Json.dll
|
||||
Microsoft.AspNetCore.Mvc.Formatters.Xml.dll
|
||||
Microsoft.AspNetCore.Mvc.Localization.dll
|
||||
Microsoft.AspNetCore.Mvc.Razor.dll
|
||||
Microsoft.AspNetCore.Mvc.RazorPages.dll
|
||||
Microsoft.AspNetCore.Mvc.TagHelpers.dll
|
||||
Microsoft.AspNetCore.Mvc.ViewFeatures.dll
|
||||
Microsoft.AspNetCore.OutputCaching.dll
|
||||
Microsoft.AspNetCore.RateLimiting.dll
|
||||
Microsoft.AspNetCore.Razor.dll
|
||||
Microsoft.AspNetCore.Razor.Runtime.dll
|
||||
Microsoft.AspNetCore.RequestDecompression.dll
|
||||
Microsoft.AspNetCore.ResponseCaching.Abstractions.dll
|
||||
Microsoft.AspNetCore.ResponseCaching.dll
|
||||
Microsoft.AspNetCore.ResponseCompression.dll
|
||||
Microsoft.AspNetCore.Rewrite.dll
|
||||
Microsoft.AspNetCore.Routing.Abstractions.dll
|
||||
Microsoft.AspNetCore.Routing.dll
|
||||
Microsoft.AspNetCore.Server.HttpSys.dll
|
||||
Microsoft.AspNetCore.Server.IIS.dll
|
||||
Microsoft.AspNetCore.Server.IISIntegration.dll
|
||||
Microsoft.AspNetCore.Server.Kestrel.Core.dll
|
||||
Microsoft.AspNetCore.Server.Kestrel.dll
|
||||
Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.dll
|
||||
Microsoft.AspNetCore.Server.Kestrel.Transport.Quic.dll
|
||||
Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll
|
||||
Microsoft.AspNetCore.Session.dll
|
||||
Microsoft.AspNetCore.SignalR.Common.dll
|
||||
Microsoft.AspNetCore.SignalR.Core.dll
|
||||
Microsoft.AspNetCore.SignalR.dll
|
||||
Microsoft.AspNetCore.SignalR.Protocols.Json.dll
|
||||
Microsoft.AspNetCore.StaticAssets.dll
|
||||
Microsoft.AspNetCore.StaticFiles.dll
|
||||
Microsoft.AspNetCore.WebSockets.dll
|
||||
Microsoft.AspNetCore.WebUtilities.dll
|
||||
Microsoft.Extensions.Caching.Abstractions.dll
|
||||
Microsoft.Extensions.Caching.Memory.dll
|
||||
Microsoft.Extensions.Configuration.Abstractions.dll
|
||||
Microsoft.Extensions.Configuration.Binder.dll
|
||||
Microsoft.Extensions.Configuration.CommandLine.dll
|
||||
Microsoft.Extensions.Configuration.dll
|
||||
Microsoft.Extensions.Configuration.EnvironmentVariables.dll
|
||||
Microsoft.Extensions.Configuration.FileExtensions.dll
|
||||
Microsoft.Extensions.Configuration.Ini.dll
|
||||
Microsoft.Extensions.Configuration.Json.dll
|
||||
Microsoft.Extensions.Configuration.KeyPerFile.dll
|
||||
Microsoft.Extensions.Configuration.UserSecrets.dll
|
||||
Microsoft.Extensions.Configuration.Xml.dll
|
||||
Microsoft.Extensions.DependencyInjection.Abstractions.dll
|
||||
Microsoft.Extensions.DependencyInjection.dll
|
||||
Microsoft.Extensions.Diagnostics.Abstractions.dll
|
||||
Microsoft.Extensions.Diagnostics.dll
|
||||
Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions.dll
|
||||
Microsoft.Extensions.Diagnostics.HealthChecks.dll
|
||||
Microsoft.Extensions.Features.dll
|
||||
Microsoft.Extensions.FileProviders.Abstractions.dll
|
||||
Microsoft.Extensions.FileProviders.Composite.dll
|
||||
Microsoft.Extensions.FileProviders.Embedded.dll
|
||||
Microsoft.Extensions.FileProviders.Physical.dll
|
||||
Microsoft.Extensions.FileSystemGlobbing.dll
|
||||
Microsoft.Extensions.Hosting.Abstractions.dll
|
||||
Microsoft.Extensions.Hosting.dll
|
||||
Microsoft.Extensions.Http.dll
|
||||
Microsoft.Extensions.Identity.Core.dll
|
||||
Microsoft.Extensions.Identity.Stores.dll
|
||||
Microsoft.Extensions.Localization.Abstractions.dll
|
||||
Microsoft.Extensions.Localization.dll
|
||||
Microsoft.Extensions.Logging.Abstractions.dll
|
||||
Microsoft.Extensions.Logging.Configuration.dll
|
||||
Microsoft.Extensions.Logging.Console.dll
|
||||
Microsoft.Extensions.Logging.Debug.dll
|
||||
Microsoft.Extensions.Logging.dll
|
||||
Microsoft.Extensions.Logging.EventLog.dll
|
||||
Microsoft.Extensions.Logging.EventSource.dll
|
||||
Microsoft.Extensions.Logging.TraceSource.dll
|
||||
Microsoft.Extensions.ObjectPool.dll
|
||||
Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
||||
Microsoft.Extensions.Options.DataAnnotations.dll
|
||||
Microsoft.Extensions.Options.dll
|
||||
Microsoft.Extensions.Primitives.dll
|
||||
Microsoft.Extensions.Validation.dll
|
||||
Microsoft.Extensions.WebEncoders.dll
|
||||
Microsoft.JSInterop.dll
|
||||
Microsoft.Net.Http.Headers.dll
|
||||
System.Diagnostics.EventLog.dll
|
||||
System.Formats.Cbor.dll
|
||||
System.Security.Cryptography.Xml.dll
|
||||
System.Threading.RateLimiting.dll
|
||||
<DOTNET>/packs/Microsoft.NETCore.App.Ref/10.0.7/ref/net10.0/
|
||||
Microsoft.CSharp.dll
|
||||
Microsoft.VisualBasic.Core.dll
|
||||
@ -232,8 +416,74 @@ obj/Debug/net10.0/
|
||||
System.Xml.XPath.dll
|
||||
System.Xml.XPath.XDocument.dll
|
||||
WindowsBase.dll
|
||||
<NUGET>/
|
||||
efcore.namingconventions/10.0.1/lib/net10.0/EFCore.NamingConventions.dll
|
||||
fastendpoints.attributes/8.1.0/lib/net10.0/FastEndpoints.Attributes.dll
|
||||
fastendpoints.core/8.1.0/lib/net10.0/FastEndpoints.Core.dll
|
||||
fastendpoints.jobqueues/8.1.0/lib/net10.0/FastEndpoints.JobQueues.dll
|
||||
fastendpoints.messaging.core/8.1.0/lib/net10.0/FastEndpoints.Messaging.Core.dll
|
||||
fastendpoints.messaging/8.1.0/lib/net10.0/FastEndpoints.Messaging.dll
|
||||
fastendpoints.security/8.1.0/lib/net10.0/FastEndpoints.Security.dll
|
||||
fastendpoints.swagger/8.1.0/lib/net10.0/FastEndpoints.Swagger.dll
|
||||
fastendpoints/8.1.0/lib/net10.0/FastEndpoints.dll
|
||||
fluentvalidation/12.1.1/lib/net8.0/FluentValidation.dll
|
||||
humanizer.core/2.14.1/lib/net6.0/Humanizer.dll
|
||||
mediatr.contracts/2.0.1/lib/netstandard2.0/MediatR.Contracts.dll
|
||||
mediatr/14.1.0/lib/net10.0/MediatR.dll
|
||||
microsoft.aspnetcore.authentication.jwtbearer/10.0.5/lib/net10.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll
|
||||
microsoft.build.framework/18.0.2/ref/net10.0/Microsoft.Build.Framework.dll
|
||||
microsoft.codeanalysis.common/5.0.0/lib/net9.0/Microsoft.CodeAnalysis.dll
|
||||
microsoft.codeanalysis.csharp.workspaces/5.0.0/lib/net9.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll
|
||||
microsoft.codeanalysis.csharp/5.0.0/lib/net9.0/Microsoft.CodeAnalysis.CSharp.dll
|
||||
microsoft.codeanalysis.workspaces.common/5.0.0/lib/net9.0/Microsoft.CodeAnalysis.Workspaces.dll
|
||||
microsoft.codeanalysis.workspaces.msbuild/5.0.0/lib/net9.0/
|
||||
Microsoft.CodeAnalysis.ExternalAccess.RazorCompiler.dll
|
||||
Microsoft.CodeAnalysis.Workspaces.MSBuild.dll
|
||||
microsoft.entityframeworkcore.abstractions/10.0.7/lib/net10.0/Microsoft.EntityFrameworkCore.Abstractions.dll
|
||||
microsoft.entityframeworkcore.design/10.0.7/lib/net10.0/Microsoft.EntityFrameworkCore.Design.dll
|
||||
microsoft.entityframeworkcore.relational/10.0.7/lib/net10.0/Microsoft.EntityFrameworkCore.Relational.dll
|
||||
microsoft.entityframeworkcore/10.0.7/lib/net10.0/Microsoft.EntityFrameworkCore.dll
|
||||
microsoft.extensions.dependencymodel/10.0.7/lib/net10.0/Microsoft.Extensions.DependencyModel.dll
|
||||
microsoft.identitymodel.abstractions/8.14.0/lib/net9.0/Microsoft.IdentityModel.Abstractions.dll
|
||||
microsoft.identitymodel.jsonwebtokens/8.14.0/lib/net9.0/Microsoft.IdentityModel.JsonWebTokens.dll
|
||||
microsoft.identitymodel.logging/8.14.0/lib/net9.0/Microsoft.IdentityModel.Logging.dll
|
||||
microsoft.identitymodel.protocols.openidconnect/8.0.1/lib/net9.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll
|
||||
microsoft.identitymodel.protocols/8.0.1/lib/net9.0/Microsoft.IdentityModel.Protocols.dll
|
||||
microsoft.identitymodel.tokens/8.14.0/lib/net9.0/Microsoft.IdentityModel.Tokens.dll
|
||||
microsoft.visualstudio.solutionpersistence/1.0.52/lib/net8.0/Microsoft.VisualStudio.SolutionPersistence.dll
|
||||
mono.texttemplating/3.0.0/lib/net6.0/Mono.TextTemplating.dll
|
||||
namotion.reflection/3.4.3/lib/net8.0/Namotion.Reflection.dll
|
||||
newtonsoft.json/13.0.3/lib/net6.0/Newtonsoft.Json.dll
|
||||
njsonschema.annotations/11.5.2/lib/netstandard2.0/NJsonSchema.Annotations.dll
|
||||
njsonschema.newtonsoftjson/11.5.2/lib/net8.0/NJsonSchema.NewtonsoftJson.dll
|
||||
njsonschema.yaml/11.5.2/lib/net8.0/NJsonSchema.Yaml.dll
|
||||
njsonschema/11.5.2/lib/net8.0/NJsonSchema.dll
|
||||
npgsql.entityframeworkcore.postgresql/10.0.1/lib/net10.0/Npgsql.EntityFrameworkCore.PostgreSQL.dll
|
||||
npgsql/10.0.2/lib/net10.0/Npgsql.dll
|
||||
nswag.annotations/14.6.3/lib/netstandard2.0/NSwag.Annotations.dll
|
||||
nswag.aspnetcore/14.6.3/lib/net10.0/NSwag.AspNetCore.dll
|
||||
nswag.core.yaml/14.6.3/lib/net8.0/NSwag.Core.Yaml.dll
|
||||
nswag.core/14.6.3/lib/net8.0/NSwag.Core.dll
|
||||
nswag.generation.aspnetcore/14.6.3/lib/net10.0/NSwag.Generation.AspNetCore.dll
|
||||
nswag.generation/14.6.3/lib/net8.0/NSwag.Generation.dll
|
||||
system.codedom/6.0.0/lib/net6.0/System.CodeDom.dll
|
||||
system.composition.attributedmodel/9.0.0/lib/net9.0/System.Composition.AttributedModel.dll
|
||||
system.composition.convention/9.0.0/lib/net9.0/System.Composition.Convention.dll
|
||||
system.composition.hosting/9.0.0/lib/net9.0/System.Composition.Hosting.dll
|
||||
system.composition.runtime/9.0.0/lib/net9.0/System.Composition.Runtime.dll
|
||||
system.composition.typedparts/9.0.0/lib/net9.0/System.Composition.TypedParts.dll
|
||||
system.identitymodel.tokens.jwt/8.0.1/lib/net9.0/System.IdentityModel.Tokens.Jwt.dll
|
||||
yamldotnet/16.3.0/lib/net8.0/YamlDotNet.dll
|
||||
|
||||
[analyzerReferences]
|
||||
<DOTNET>/packs/Microsoft.AspNetCore.App.Ref/10.0.7/analyzers/dotnet/cs/
|
||||
Microsoft.AspNetCore.App.Analyzers.dll
|
||||
Microsoft.AspNetCore.App.CodeFixes.dll
|
||||
Microsoft.AspNetCore.App.SourceGenerators.dll
|
||||
Microsoft.AspNetCore.Components.Analyzers.dll
|
||||
Microsoft.Extensions.Logging.Generators.dll
|
||||
Microsoft.Extensions.Options.SourceGeneration.dll
|
||||
Microsoft.Extensions.Validation.ValidationsGenerator.dll
|
||||
<DOTNET>/packs/Microsoft.NETCore.App.Ref/10.0.7/analyzers/dotnet/cs/
|
||||
Microsoft.Interop.ComInterfaceGenerator.dll
|
||||
Microsoft.Interop.JavaScript.JSImportGenerator.dll
|
||||
@ -241,9 +491,20 @@ obj/Debug/net10.0/
|
||||
Microsoft.Interop.SourceGeneration.dll
|
||||
System.Text.Json.SourceGeneration.dll
|
||||
System.Text.RegularExpressions.Generator.dll
|
||||
<DOTNET>/sdk/10.0.203/Sdks/Microsoft.NET.Sdk.Razor/source-generators/
|
||||
Microsoft.AspNetCore.Razor.Utilities.Shared.dll
|
||||
Microsoft.CodeAnalysis.Razor.Compiler.dll
|
||||
Microsoft.Extensions.ObjectPool.dll
|
||||
<DOTNET>/sdk/10.0.203/Sdks/Microsoft.NET.Sdk.Web/analyzers/cs/
|
||||
Microsoft.AspNetCore.Analyzers.dll
|
||||
Microsoft.AspNetCore.Mvc.Analyzers.dll
|
||||
<DOTNET>/sdk/10.0.203/Sdks/Microsoft.NET.Sdk/analyzers/
|
||||
Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll
|
||||
Microsoft.CodeAnalysis.NetAnalyzers.dll
|
||||
<NUGET>/microsoft.codeanalysis.analyzers/3.11.0/analyzers/dotnet/cs/
|
||||
Microsoft.CodeAnalysis.Analyzers.dll
|
||||
Microsoft.CodeAnalysis.CSharp.Analyzers.dll
|
||||
<NUGET>/microsoft.entityframeworkcore.analyzers/10.0.7/analyzers/dotnet/cs/Microsoft.EntityFrameworkCore.Analyzers.dll
|
||||
|
||||
[analyzerConfigFiles]
|
||||
<DOTNET>/sdk/10.0.203/Sdks/Microsoft.NET.Sdk/analyzers/build/config/analysislevel_10_default.globalconfig
|
||||
|
||||
17
src/Workflow.Api/appsettings.json
Normal file
17
src/Workflow.Api/appsettings.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"Default": "Host=localhost;Port=5432;Database=workflow;Username=postgres;Password=postgres"
|
||||
},
|
||||
"Jwt": {
|
||||
"Issuer": "workflow-api",
|
||||
"Audience": "workflow-api",
|
||||
"SigningKey": "workflow-engine-secret-key-at-least-32-characters-long"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
using MediatR;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Workflow.Application.Features.WorkflowInstances.DTOs;
|
||||
using Workflow.Domain.Enums;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
|
||||
using TaskStatus = Workflow.Domain.Enums.TaskStatus;
|
||||
|
||||
namespace Workflow.Application.Features.WorkflowInstances.Queries;
|
||||
|
||||
public record MonitorWorkflowInstancesQuery : IRequest<WorkflowMonitorDto>;
|
||||
|
||||
@ -3,6 +3,8 @@ using Workflow.Domain.Enums;
|
||||
using Workflow.Domain.Exceptions;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
|
||||
using TaskStatus = Workflow.Domain.Enums.TaskStatus;
|
||||
|
||||
namespace Workflow.Application.Features.WorkflowTasks.Commands;
|
||||
|
||||
public record UrgeTaskCommand(
|
||||
|
||||
@ -5,6 +5,8 @@ using Workflow.Application.Features.WorkflowTasks.DTOs;
|
||||
using Workflow.Domain.Enums;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
|
||||
using TaskStatus = Workflow.Domain.Enums.TaskStatus;
|
||||
|
||||
namespace Workflow.Application.Features.WorkflowTasks.Queries;
|
||||
|
||||
public record GetOverdueTasksQuery(
|
||||
|
||||
@ -8,6 +8,7 @@ using Workflow.Domain.Enums;
|
||||
using Workflow.Domain.Exceptions;
|
||||
using Workflow.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Xunit;
|
||||
|
||||
public class ProcessEngineTests
|
||||
{
|
||||
@ -86,9 +87,9 @@ public class ProcessEngineTests
|
||||
.ToListAsync();
|
||||
|
||||
tasks.Should().HaveCount(1);
|
||||
tasks[0].AssigneeId.Should().Be("user-001");
|
||||
tasks[0].AssigneeRole.Should().Be("user-001");
|
||||
tasks[0].Type.Should().Be(TaskType.Approval);
|
||||
tasks[0].Status.Should().Be(Enums.TaskStatus.Pending);
|
||||
tasks[0].Status.Should().Be(TaskStatus.Pending);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -110,7 +111,7 @@ public class ProcessEngineTests
|
||||
tasks.Should().HaveCount(1);
|
||||
tasks[0].AssigneeRole.Should().Be("manager");
|
||||
tasks[0].Type.Should().Be(TaskType.Approval);
|
||||
tasks[0].Status.Should().Be(Enums.TaskStatus.Pending);
|
||||
tasks[0].Status.Should().Be(TaskStatus.Pending);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -131,10 +132,10 @@ public class ProcessEngineTests
|
||||
Id = Guid.NewGuid(),
|
||||
InstanceId = instance.Id,
|
||||
TokenId = token.Id,
|
||||
NodeId = approvalNode.Id.ToString(),
|
||||
AssigneeId = "user-001",
|
||||
NodeId = approvalNode.Id,
|
||||
AssigneeRole = "user-001",
|
||||
Type = TaskType.Approval,
|
||||
Status = Enums.TaskStatus.Pending,
|
||||
Status = TaskStatus.Pending,
|
||||
};
|
||||
_dbContext.WorkflowTasks.Add(task);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
@ -167,10 +168,10 @@ public class ProcessEngineTests
|
||||
Id = Guid.NewGuid(),
|
||||
InstanceId = instance.Id,
|
||||
TokenId = token.Id,
|
||||
NodeId = approvalNode.Id.ToString(),
|
||||
AssigneeId = "user-001",
|
||||
NodeId = approvalNode.Id,
|
||||
AssigneeRole = "user-001",
|
||||
Type = TaskType.Approval,
|
||||
Status = Enums.TaskStatus.Pending,
|
||||
Status = TaskStatus.Pending,
|
||||
};
|
||||
_dbContext.WorkflowTasks.Add(task);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
@ -203,10 +204,10 @@ public class ProcessEngineTests
|
||||
Id = Guid.NewGuid(),
|
||||
InstanceId = instance.Id,
|
||||
TokenId = token.Id,
|
||||
NodeId = approvalNode.Id.ToString(),
|
||||
AssigneeId = "user-001",
|
||||
NodeId = approvalNode.Id,
|
||||
AssigneeRole = "user-001",
|
||||
Type = TaskType.Approval,
|
||||
Status = Enums.TaskStatus.Pending,
|
||||
Status = TaskStatus.Pending,
|
||||
};
|
||||
_dbContext.WorkflowTasks.Add(task);
|
||||
await _dbContext.SaveChangesAsync();
|
||||
@ -533,7 +534,7 @@ public class ProcessEngineTests
|
||||
{
|
||||
var subDefId = Guid.NewGuid();
|
||||
var subProcessNode = CreateNode(NodeType.SubProcess, "sub-1");
|
||||
subProcessNode.Config = $"{{ "definitionId": "{subDefId}" }}";
|
||||
subProcessNode.Config = $"{{ \"definitionId\": \"{subDefId}\" }}";
|
||||
|
||||
var definition = CreateDefinition(subProcessNode);
|
||||
var instance = CreateInstance(definition);
|
||||
|
||||
@ -57,6 +57,7 @@ TemporaryDependencyNodeTargetIdentifier=net10.0
|
||||
<NUGET>/microsoft.net.test.sdk/18.5.1/build/net8.0/Microsoft.NET.Test.Sdk.Program.cs
|
||||
@folderNames=..,..,..,..,..,.nuget,packages,microsoft.net.test.sdk,18.5.1,build,net8.0
|
||||
Condition/ConditionEvaluatorTests.cs
|
||||
Engine/ProcessEngineTests.cs
|
||||
obj/Debug/net10.0/
|
||||
.NETCoreApp,Version=v10.0.AssemblyAttributes.cs
|
||||
Workflow.Tests.AssemblyInfo.cs
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user