diff --git a/Controllers/BaseController.cs b/Controllers/BaseController.cs new file mode 100644 index 0000000..c529eda --- /dev/null +++ b/Controllers/BaseController.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc; + +namespace p_designer.controllers +{ + [ApiController] + [Route("api")] + public class BaseController : ControllerBase + { + } +} diff --git a/Controllers/DictionaryController.cs b/Controllers/DictionaryController.cs new file mode 100644 index 0000000..5460480 --- /dev/null +++ b/Controllers/DictionaryController.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; +using p_designer.entities; +using p_designer.Models; +using p_designer.services; +using Swashbuckle.AspNetCore.Annotations; + +namespace p_designer.controllers +{ + public class DictionaryController : BaseController + { + private DictionaryService dictionaryService { get; set; } + public DictionaryController(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + [HttpGet] + [Route("lifecycle-statuses")] + [SwaggerOperation(Summary = "Статусы жизненного цикла")] + public async Task> GetLifecycleStatuses() + { + return await dictionaryService.GetLifecycleStatusesAsync(); + } + + [HttpGet] + [Route("aspect-levels")] + [SwaggerOperation(Summary = "Уровни аспектов")] + public async Task> GetAspectLevels() + { + return await dictionaryService.GetAspectLevelsAsync(); + } + } +} diff --git a/Controllers/PatternController.cs b/Controllers/PatternController.cs new file mode 100644 index 0000000..a76a39b --- /dev/null +++ b/Controllers/PatternController.cs @@ -0,0 +1,66 @@ +using Microsoft.AspNetCore.Mvc; +using p_designer.Models; +using p_designer.services; +using Swashbuckle.AspNetCore.Annotations; +using System.ComponentModel.DataAnnotations; + +namespace p_designer.controllers +{ + public class PatternController : BaseController + { + private PatternService patternService { get; set; } + + public PatternController(PatternService patternService) + { + this.patternService = patternService; + } + + [HttpGet] + [Route("patterns")] + [SwaggerOperation(Summary = "Получить страницу паттернов")] + public async Task> ReadPatternsPage([Required] int page, [Required] int pageSize) + { + return await patternService.ReadPageAsync(page, pageSize); + } + + [HttpGet] + [Route("pattern")] + [SwaggerOperation(Summary = "Получить паттерн")] + public async Task ReadPattern([Required] int id) + { + return await patternService.ReadAsync(id); + } + + [HttpPost] + [Route("pattern")] + [SwaggerOperation(Summary = "Создать паттерн")] + public async Task CreatePattern(PatternModel.Create pattern) + { + await patternService.CreateAsync(pattern); + } + + [HttpPut] + [Route("pattern")] + [SwaggerOperation(Summary = "Изменить паттерн")] + public async Task UpdatePattern(PatternModel.Update pattern) + { + await patternService.UpdateAsync(pattern); + } + + [HttpDelete] + [Route("pattern/remove")] + [SwaggerOperation(Summary = "Изменить статус паттерна на deleted")] + public async Task RemoveAsync([Required] int id) + { + await patternService.RemoveAsync(id); + } + + [HttpDelete] + [Route("pattern/delete")] + [SwaggerOperation(Summary = "Удалить паттерн из базы данных")] + public async Task DeleteAsync([Required] int id) + { + await patternService.DeleteAsync(id); + } + } +} diff --git a/Models/DictionaryModel.cs b/Models/DictionaryModel.cs new file mode 100644 index 0000000..44e43c0 --- /dev/null +++ b/Models/DictionaryModel.cs @@ -0,0 +1,8 @@ +namespace p_designer.Models +{ + public class DictionaryModel + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/Models/MetaDataModel.cs b/Models/MetaDataModel.cs new file mode 100644 index 0000000..da82061 --- /dev/null +++ b/Models/MetaDataModel.cs @@ -0,0 +1,17 @@ +namespace p_designer.Models +{ + public class MetaDataModel + { + public IEnumerable Data { get; set; } + public Meta Meta { get; set; } + } + + public class Meta + { + public int TotalItemsNumber { get; set; } + public int PagesNumber { get; set; } + public int CurrentPage { get; set; } + public bool HasNextPage { get; set; } + public bool HasPreviousPage { get; set; } + } +} diff --git a/Models/PatternModel.cs b/Models/PatternModel.cs new file mode 100644 index 0000000..4f9c9be --- /dev/null +++ b/Models/PatternModel.cs @@ -0,0 +1,43 @@ +namespace p_designer.Models +{ + public class PatternModel + { + public class Read + { + public class Long + { + public int Id { get; set; } + public string Name { get; set; } + public int LifecycleStatusId { get; set; } + public double ProjectValueMin { get; set; } + public double ProjectValueMax { get; set; } + public double ProjectValueTarget { get; set; } + } + + public class Short + { + public int Id { get; set; } + public string Name { get; set; } + } + } + + public class Create + { + public string Name { get; set; } + public int LifecycleStatusId { get; set; } + public double ProjectValueMin { get; set; } + public double ProjectValueMax { get; set; } + public double ProjectValueTarget { get; set; } + } + + public class Update + { + public int Id { get; set; } + public string Name { get; set; } + public int LifecycleStatusId { get; set; } + public double ProjectValueMin { get; set; } + public double ProjectValueMax { get; set; } + public double ProjectValueTarget { get; set; } + } + } +} diff --git a/Models/enums/AspectLevelEnum.cs b/Models/enums/AspectLevelEnum.cs new file mode 100644 index 0000000..147f6d0 --- /dev/null +++ b/Models/enums/AspectLevelEnum.cs @@ -0,0 +1,12 @@ +namespace p_designer.Models.enums +{ + public enum AspectLevelEnum + { + BusinessLayer = 1, + FunctionalLayer = 2, + InformationLayer = 3, + CommunicationLayer = 4, + IntegrationLayer = 5, + PhysicalLayer = 6 + } +} diff --git a/Models/enums/LifecycleStatusEnum.cs b/Models/enums/LifecycleStatusEnum.cs new file mode 100644 index 0000000..0b868cf --- /dev/null +++ b/Models/enums/LifecycleStatusEnum.cs @@ -0,0 +1,9 @@ +namespace p_designer.Models.enums +{ + public enum LifecycleStatusEnum + { + Draft = 1, + ReadyToUse = 2, + Deleted = 3 + } +} diff --git a/Program.cs b/Program.cs index ba48166..d4103a5 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,7 @@ +using Microsoft.OpenApi.Models; using p_designer.entities; +using p_designer.services; +using Swashbuckle.AspNetCore.SwaggerUI; var builder = WebApplication.CreateBuilder(args); @@ -11,13 +14,38 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddDbContext(); + +builder.Services.AddTransient(); +builder.Services.AddTransient(); + +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc("v1", new OpenApiInfo { Title = "p-designer", Version = "v1" }); + c.CustomSchemaIds(type => type.ToString()); + c.EnableAnnotations(); +}); + var app = builder.Build(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwaggerUI(x => + { + x.SwaggerEndpoint("/swagger/v1/swagger.json", "p-designer API v1"); + x.DefaultModelExpandDepth(3); + x.DefaultModelRendering(ModelRendering.Example); + x.DefaultModelsExpandDepth(-1); + x.DisplayOperationId(); + x.DisplayRequestDuration(); + x.DocExpansion(docExpansion: DocExpansion.None); + x.EnableDeepLinking(); + x.EnableFilter(); + x.ShowExtensions(); + }); } app.UseHttpsRedirection(); diff --git a/Services/DictionaryService.cs b/Services/DictionaryService.cs new file mode 100644 index 0000000..826f8c7 --- /dev/null +++ b/Services/DictionaryService.cs @@ -0,0 +1,26 @@ +using Mapster; +using p_designer.entities; +using p_designer.Models; + +namespace p_designer.services +{ + public class DictionaryService + { + private PDesignerContext context { get; set; } + + public DictionaryService(PDesignerContext context) + { + this.context = context; + } + + public Task> GetLifecycleStatusesAsync() + { + return Task.FromResult(context.LifecycleStatuses.ProjectToType()); + } + + public Task> GetAspectLevelsAsync() + { + return Task.FromResult(context.AspectLevels.ProjectToType()); + } + } +} diff --git a/Services/PatternService.cs b/Services/PatternService.cs new file mode 100644 index 0000000..dff72b5 --- /dev/null +++ b/Services/PatternService.cs @@ -0,0 +1,62 @@ +using Mapster; +using Microsoft.EntityFrameworkCore; +using p_designer.common; +using p_designer.common.extensions; +using p_designer.entities; +using p_designer.Models; +using p_designer.Models.enums; + +namespace p_designer.services +{ + public class PatternService + { + private PDesignerContext context { get; set; } + public PatternService(PDesignerContext context) + { + this.context = context; + } + + public async Task ReadAsync(int id) + { + return await context.Patterns + .ProjectToType() + .SingleAsync(p => p.Id == id); + } + + public async Task> ReadPageAsync(int page, int pageSize) + { + var data = context.Patterns.Where(p => p.LifecycleStatusId != (int)LifecycleStatusEnum.Deleted) + .ProjectToType(); + var factory = new MetaDataFactory(data); + return await factory.CreateAsync(page, pageSize); + } + + public async Task CreateAsync(PatternModel.Create patternModel) + { + var pattern = patternModel.Adapt(); + await context.AddAsync(pattern); + await context.SaveChangesAsync(); + } + + public async Task UpdateAsync(PatternModel.Update patternModel) + { + var pattern = patternModel.Adapt(); + context.Update(pattern); + await context.SaveChangesAsync(); + } + + public async Task RemoveAsync(int id) + { + var pattern = await context.Patterns.FindAsync(id); + pattern.LifecycleStatusId = (int)LifecycleStatusEnum.Deleted; + await context.SaveChangesAsync(); + } + + public async Task DeleteAsync(int id) + { + var pattern = await context.Patterns.FindAsync(id); + context.Remove(pattern); + await context.SaveChangesAsync(); + } + } +} diff --git a/common/MetaDataFactory.cs b/common/MetaDataFactory.cs new file mode 100644 index 0000000..c42823b --- /dev/null +++ b/common/MetaDataFactory.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using p_designer.common; +using p_designer.common.extensions; +using p_designer.Models; + +namespace p_designer.common +{ + public class MetaDataFactory + { + private IQueryable data { get; set; } + public MetaDataFactory(IQueryable data) + { + this.data = data; + } + + public async Task> CreateAsync(int page, int pageSize) + { + var meta = new Meta(); + var pagedData = data.GetPage(page, pageSize); + + meta.TotalItemsNumber = await data.CountAsync(); + meta.PagesNumber = PagesCounter.GetPagesNumber(pageSize, meta.TotalItemsNumber); + meta.CurrentPage = page; + meta.HasNextPage = meta.CurrentPage < meta.PagesNumber; + meta.HasPreviousPage = meta.CurrentPage > 1; + + return new MetaDataModel() + { + Data = pagedData, + Meta = meta + }; + } + } +} diff --git a/common/PagesCounter.cs b/common/PagesCounter.cs new file mode 100644 index 0000000..6e6a404 --- /dev/null +++ b/common/PagesCounter.cs @@ -0,0 +1,13 @@ +namespace p_designer.common +{ + public static class PagesCounter + { + public static int GetPagesNumber(int pagesSize, int totalItemsNumber) + { + var pages = totalItemsNumber / pagesSize + 1; + if (totalItemsNumber % pagesSize == 0) + pages--; + return pages; + } + } +} diff --git a/common/extensions/IQueryableExtensions.cs b/common/extensions/IQueryableExtensions.cs new file mode 100644 index 0000000..65b7c45 --- /dev/null +++ b/common/extensions/IQueryableExtensions.cs @@ -0,0 +1,13 @@ +namespace p_designer.common.extensions +{ + public static class IQueryableExtensions + { + public static IQueryable GetPage(this IQueryable query, int page, int pageSize) + { + if (pageSize > 200) + throw new Exception("Page size must be lower than 200"); + + return query.Skip((page - 1) * pageSize).Take(pageSize); + } + } +} diff --git a/entities/Pattern.cs b/entities/Pattern.cs index ab76865..656dbd2 100644 --- a/entities/Pattern.cs +++ b/entities/Pattern.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace p_designer.entities { @@ -12,6 +13,8 @@ public Pattern() [Key] public int Id { get; set; } + [ForeignKey(nameof(LifecycleStatus))] + public int LifecycleStatusId { get; set; } [Required] public string Name { get; set; } public double ProjectValueMax { get; set; } diff --git a/p-designer.csproj b/p-designer.csproj index 5396947..01cf74e 100644 --- a/p-designer.csproj +++ b/p-designer.csproj @@ -22,14 +22,11 @@ + - - - - - +