Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions SetupProject.sln
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F15", "Src\Core\F15\F15.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F16", "Src\Core\F16\F16.csproj", "{7373234F-7D82-49D3-BBCB-B8CFD84F6389}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "F17", "Src\Core\F17\F17.csproj", "{A4CC6A27-4EDE-4383-9956-B41A622D34D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -161,6 +163,10 @@ Global
{7373234F-7D82-49D3-BBCB-B8CFD84F6389}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7373234F-7D82-49D3-BBCB-B8CFD84F6389}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7373234F-7D82-49D3-BBCB-B8CFD84F6389}.Release|Any CPU.Build.0 = Release|Any CPU
{A4CC6A27-4EDE-4383-9956-B41A622D34D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4CC6A27-4EDE-4383-9956-B41A622D34D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4CC6A27-4EDE-4383-9956-B41A622D34D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4CC6A27-4EDE-4383-9956-B41A622D34D3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -193,5 +199,6 @@ Global
{C044FCB9-18E7-4EE2-AA37-19E658387971} = {F7C07F6C-C2ED-4E06-9025-AD33B4BBBB51}
{5F6FC9E6-C713-471B-99E8-AACC3994C583} = {F7C07F6C-C2ED-4E06-9025-AD33B4BBBB51}
{7373234F-7D82-49D3-BBCB-B8CFD84F6389} = {F7C07F6C-C2ED-4E06-9025-AD33B4BBBB51}
{A4CC6A27-4EDE-4383-9956-B41A622D34D3} = {F7C07F6C-C2ED-4E06-9025-AD33B4BBBB51}
EndGlobalSection
EndGlobal
43 changes: 43 additions & 0 deletions Src/Core/F17/BusinessLogic/F17Service.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using F17.Common;
using F17.DataAccess;
using F17.Models;
using FCommon.FeatureService;

namespace F17.BusinessLogic;

public sealed class F17Service : IServiceHandler<F17AppRequestModel, F17AppResponseModel>
{
private readonly Lazy<IF17Repository> _repository;

public F17Service(Lazy<IF17Repository> repository)
{
_repository = repository;
}

public async Task<F17AppResponseModel> ExecuteAsync(
F17AppRequestModel request,
CancellationToken ct
)
{
var doesTaskExist = await _repository.Value.DoesTodoTaskExistAsync(request.TodoTaskId, ct);
if (!doesTaskExist)
{
return F17Constant.DefaultResponse.App.TASK_NOT_FOUND;
}

var isSuccess = await _repository.Value.ModifyCompletedStatusAsync(
request.TodoTaskId,
request.IsCompleted,
ct
);
if (!isSuccess)
{
return F17Constant.DefaultResponse.App.SERVER_ERROR;
}

return new() { AppCode = F17Constant.AppCode.SUCCESS };
}
}
60 changes: 60 additions & 0 deletions Src/Core/F17/Common/F17Constant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using F17.Models;
using F17.Presentation;
using Microsoft.AspNetCore.Http;

namespace F17.Common;

public static class F17Constant
{
public const string ENDPOINT_PATH = "f17";

public const string REQUEST_ARGUMENT_NAME = "request";

public static class DefaultResponse
{
public static class App
{
public static readonly F17AppResponseModel TASK_NOT_FOUND = new()
{
AppCode = AppCode.TASK_NOT_FOUND,
};

public static readonly F17AppResponseModel SERVER_ERROR = new()
{
AppCode = AppCode.SERVER_ERROR,
};
}

public static class Http
{
public static readonly F17Response VALIDATION_FAILED = new()
{
AppCode = (int)AppCode.VALIDATION_FAILED,
HttpCode = StatusCodes.Status400BadRequest,
};

public static readonly F17Response TASK_NOT_FOUND = new()
{
AppCode = (int)AppCode.TASK_NOT_FOUND,
HttpCode = StatusCodes.Status404NotFound,
};

public static readonly F17Response SERVER_ERROR = new()
{
AppCode = (int)AppCode.SERVER_ERROR,
HttpCode = StatusCodes.Status500InternalServerError,
};
}
}

public enum AppCode
{
SUCCESS = 1,

VALIDATION_FAILED,

TASK_NOT_FOUND,

SERVER_ERROR,
}
}
65 changes: 65 additions & 0 deletions Src/Core/F17/DataAccess/F17Repository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FA1.DbContext;
using FA1.Entities;
using Microsoft.EntityFrameworkCore;

namespace F17.DataAccess;

public sealed class F17Repository : IF17Repository
{
private readonly AppDbContext _appContext;

public F17Repository(AppDbContext context)
{
_appContext = context;
}

public async Task<bool> ModifyCompletedStatusAsync(
long taskId,
bool isCompleted,
CancellationToken ct
)
{
var dbResult = true;

await _appContext
.Database.CreateExecutionStrategy()
.ExecuteAsync(async () =>
{
await using var dbTransaction = await _appContext.Database.BeginTransactionAsync(
IsolationLevel.ReadCommitted,
ct
);

try
{
await _appContext
.Set<TodoTaskEntity>()
.Where(task => task.Id == taskId)
.ExecuteUpdateAsync(
setProp =>
setProp.SetProperty(entity => entity.IsFinished, isCompleted),
ct
);

await dbTransaction.CommitAsync(ct);
}
catch (DbUpdateException)
{
await dbTransaction.RollbackAsync(ct);

dbResult = false;
}
});

return dbResult;
}

public Task<bool> DoesTodoTaskExistAsync(long taskId, CancellationToken ct)
{
return _appContext.Set<TodoTaskEntity>().AnyAsync(entity => entity.Id == taskId, ct);
}
}
11 changes: 11 additions & 0 deletions Src/Core/F17/DataAccess/IF17Repository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Threading;
using System.Threading.Tasks;

namespace F17.DataAccess;

public interface IF17Repository
{
Task<bool> DoesTodoTaskExistAsync(long taskId, CancellationToken ct);

Task<bool> ModifyCompletedStatusAsync(long taskId, bool isCompleted, CancellationToken ct);
}
2 changes: 2 additions & 0 deletions Src/Core/F17/F17.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<Project Sdk="Microsoft.NET.Sdk">
</Project>
33 changes: 33 additions & 0 deletions Src/Core/F17/F17Register.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using F17.BusinessLogic;
using F17.DataAccess;
using FACommon.DependencyInjection;
using FluentValidation;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace F17;

public sealed class F17Register : IServiceRegister
{
public IServiceCollection Register(IServiceCollection services, IConfiguration configuration)
{
var currentAssembly = typeof(F17Register).Assembly;

#region Filters
services.RegisterFiltersFromAssembly(currentAssembly);
#endregion

#region Validation
services.AddValidatorsFromAssembly(currentAssembly, ServiceLifetime.Singleton);
#endregion

#region Core
services
.AddScoped<IF17Repository, F17Repository>()
.MakeScopedLazy<IF17Repository>()
.AddScoped<F17Service>();
#endregion

return services;
}
}
51 changes: 51 additions & 0 deletions Src/Core/F17/Mapper/F17HttpResponseMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System;
using System.Collections.Concurrent;
using F17.Common;
using F17.Models;
using F17.Presentation;
using Microsoft.AspNetCore.Http;

namespace F17.Mapper;

public static class F17HttpResponseMapper
{
private static ConcurrentDictionary<
F17Constant.AppCode,
Func<F17AppRequestModel, F17AppResponseModel, F17Response>
> _httpResponseMapper;

private static void Init()
{
if (Equals(_httpResponseMapper, null))
{
_httpResponseMapper = new();
}

_httpResponseMapper.TryAdd(
F17Constant.AppCode.SUCCESS,
(appRequest, appResponse) =>
new()
{
AppCode = (int)F17Constant.AppCode.SUCCESS,
HttpCode = StatusCodes.Status200OK,
}
);

_httpResponseMapper.TryAdd(
F17Constant.AppCode.TASK_NOT_FOUND,
(appRequest, appResponse) => F17Constant.DefaultResponse.Http.TASK_NOT_FOUND
);

_httpResponseMapper.TryAdd(
F17Constant.AppCode.SERVER_ERROR,
(appRequest, appResponse) => F17Constant.DefaultResponse.Http.SERVER_ERROR
);
}

public static F17Response Get(F17AppRequestModel appRequest, F17AppResponseModel appResponse)
{
Init();

return _httpResponseMapper[appResponse.AppCode](appRequest, appResponse);
}
}
10 changes: 10 additions & 0 deletions Src/Core/F17/Models/F17AppRequestModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using FCommon.FeatureService;

namespace F17.Models;

public sealed class F17AppRequestModel : IServiceRequest<F17AppResponseModel>
{
public long TodoTaskId { get; set; }

public bool IsCompleted { get; set; }
}
13 changes: 13 additions & 0 deletions Src/Core/F17/Models/F17AppResponseModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using F17.Common;
using FCommon.FeatureService;

namespace F17.Models;

public sealed class F17AppResponseModel : IServiceResponse
{
public F17Constant.AppCode AppCode { get; set; }

public BodyModel Body { get; set; }

public sealed class BodyModel { }
}
42 changes: 42 additions & 0 deletions Src/Core/F17/Presentation/F17Endpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Threading;
using System.Threading.Tasks;
using F17.BusinessLogic;
using F17.Common;
using F17.Mapper;
using F17.Models;
using F17.Presentation.Filters.Validation;
using FCommon.Authorization.Default;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace F17.Presentation;

public sealed class F17Endpoint : ControllerBase
{
private readonly F17Service _service;

public F17Endpoint(F17Service service)
{
_service = service;
}

[HttpPost(F17Constant.ENDPOINT_PATH)]
[Authorize(Policy = nameof(DefaultAuthorizationRequirement))]
[ServiceFilter<F17ValidationFilter>(Order = 1)]
public async Task<IActionResult> ExecuteAsync(
[FromBody] F17Request request,
CancellationToken ct
)
{
var appRequest = new F17AppRequestModel
{
TodoTaskId = request.TodoTaskId,
IsCompleted = request.IsCompleted,
};
var appResponse = await _service.ExecuteAsync(appRequest, ct);

var httpResponse = F17HttpResponseMapper.Get(appRequest, appResponse);

return StatusCode(httpResponse.HttpCode, httpResponse);
}
}
8 changes: 8 additions & 0 deletions Src/Core/F17/Presentation/F17Request.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace F17.Presentation;

public sealed class F17Request
{
public long TodoTaskId { get; set; }

public bool IsCompleted { get; set; }
}
15 changes: 15 additions & 0 deletions Src/Core/F17/Presentation/F17Response.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace F17.Presentation;

public sealed class F17Response
{
[JsonIgnore]
public int HttpCode { get; set; }

public int AppCode { get; set; }

public BodyDto Body { get; set; }

public sealed class BodyDto { }
}
Loading
Loading