From 0f645b4925321b020b9644a2280d74bb81153b77 Mon Sep 17 00:00:00 2001 From: NursultanBekenov Date: Wed, 7 Jun 2023 01:07:05 +0500 Subject: [PATCH] =?UTF-8?q?[Task-1]:=20added=20swagger,=20logger.=20DAL=20?= =?UTF-8?q?and=20BLL=20=D0=BD=D0=B0=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA?= =?UTF-8?q?=D0=BE=20=D1=81=D0=BC=D0=BE=D0=B3.=20DB.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Controllers/TodoItemsController.cs | 48 +++++++++++++++++------------- {Models => DL}/TodoContext.cs | 3 +- {Models => DTO}/TodoItemDTO.cs | 4 +-- Logger/FileLogger.cs | 38 +++++++++++++++++++++++ Logger/FileLoggerExtensions.cs | 14 +++++++++ Logger/FileLoggerProvider.cs | 22 ++++++++++++++ Models/TodoItem.cs | 4 +-- Repo/ITodoRepository.cs | 16 ++++++++++ Repo/TodoRepository.cs | 47 +++++++++++++++++++++++++++++ Startup.cs | 32 +++++++++++++++++--- appsettings.Development.json | 3 ++ 11 files changed, 200 insertions(+), 31 deletions(-) rename {Models => DL}/TodoContext.cs (83%) rename {Models => DTO}/TodoItemDTO.cs (87%) create mode 100644 Logger/FileLogger.cs create mode 100644 Logger/FileLoggerExtensions.cs create mode 100644 Logger/FileLoggerProvider.cs create mode 100644 Repo/ITodoRepository.cs create mode 100644 Repo/TodoRepository.cs diff --git a/Controllers/TodoItemsController.cs b/Controllers/TodoItemsController.cs index 0ef138e7..ffd9c3a5 100644 --- a/Controllers/TodoItemsController.cs +++ b/Controllers/TodoItemsController.cs @@ -3,7 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using TodoApi.Models; +using TodoApiDTO.Models; +using TodoApiDTO.Repositories; +using Microsoft.Extensions.Logging; + +using TodoApiDTO.DTO; namespace TodoApi.Controllers { @@ -11,25 +15,28 @@ namespace TodoApi.Controllers [ApiController] public class TodoItemsController : ControllerBase { - private readonly TodoContext _context; + private readonly ILogger _logger; + private readonly ITodoRepository _repository; - public TodoItemsController(TodoContext context) + public TodoItemsController(ILoggerFactory loggerFactory, + ITodoRepository repository) { - _context = context; + _logger = loggerFactory.CreateLogger("FileLogger"); + _repository = repository; } [HttpGet] public async Task>> GetTodoItems() { - return await _context.TodoItems - .Select(x => ItemToDTO(x)) - .ToListAsync(); + var todoItems = await _repository.GetTodoItemsAsync(); + + return todoItems.Select(x => ItemToDTO(x)).ToArray(); } [HttpGet("{id}")] public async Task> GetTodoItem(long id) { - var todoItem = await _context.TodoItems.FindAsync(id); + var todoItem = await _repository.GetTodoItemAsync(id); if (todoItem == null) { @@ -47,7 +54,7 @@ public async Task UpdateTodoItem(long id, TodoItemDTO todoItemDTO return BadRequest(); } - var todoItem = await _context.TodoItems.FindAsync(id); + var todoItem = await _repository.GetTodoItemAsync(id); if (todoItem == null) { return NotFound(); @@ -58,10 +65,12 @@ public async Task UpdateTodoItem(long id, TodoItemDTO todoItemDTO try { - await _context.SaveChangesAsync(); + await _repository.SaveChangesAsync(); } - catch (DbUpdateConcurrencyException) when (!TodoItemExists(id)) + catch (DbUpdateConcurrencyException) when (!_repository.TodoItemExists(id)) { + _logger.LogError($"Item not found by id {id}"); + return NotFound(); } @@ -77,8 +86,8 @@ public async Task> CreateTodoItem(TodoItemDTO todoItem Name = todoItemDTO.Name }; - _context.TodoItems.Add(todoItem); - await _context.SaveChangesAsync(); + _repository.CreateTodoItem(todoItem); + await _repository.SaveChangesAsync(); return CreatedAtAction( nameof(GetTodoItem), @@ -89,28 +98,25 @@ public async Task> CreateTodoItem(TodoItemDTO todoItem [HttpDelete("{id}")] public async Task DeleteTodoItem(long id) { - var todoItem = await _context.TodoItems.FindAsync(id); + var todoItem = await _repository.GetTodoItemAsync(id); if (todoItem == null) { return NotFound(); } - _context.TodoItems.Remove(todoItem); - await _context.SaveChangesAsync(); + _repository.DeleteTodoItem(todoItem); + await _repository.SaveChangesAsync(); return NoContent(); } - private bool TodoItemExists(long id) => - _context.TodoItems.Any(e => e.Id == id); - private static TodoItemDTO ItemToDTO(TodoItem todoItem) => new TodoItemDTO { Id = todoItem.Id, Name = todoItem.Name, IsComplete = todoItem.IsComplete - }; + }; } -} +} \ No newline at end of file diff --git a/Models/TodoContext.cs b/DL/TodoContext.cs similarity index 83% rename from Models/TodoContext.cs rename to DL/TodoContext.cs index 6e59e363..1a713f2b 100644 --- a/Models/TodoContext.cs +++ b/DL/TodoContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; +using TodoApiDTO.Models; -namespace TodoApi.Models +namespace TodoApiDTO.Data { public class TodoContext : DbContext { diff --git a/Models/TodoItemDTO.cs b/DTO/TodoItemDTO.cs similarity index 87% rename from Models/TodoItemDTO.cs rename to DTO/TodoItemDTO.cs index e66a500a..ec115706 100644 --- a/Models/TodoItemDTO.cs +++ b/DTO/TodoItemDTO.cs @@ -1,4 +1,4 @@ -namespace TodoApi.Models +namespace TodoApiDTO.DTO { #region snippet public class TodoItemDTO @@ -8,4 +8,4 @@ public class TodoItemDTO public bool IsComplete { get; set; } } #endregion -} +} \ No newline at end of file diff --git a/Logger/FileLogger.cs b/Logger/FileLogger.cs new file mode 100644 index 00000000..03e85fe3 --- /dev/null +++ b/Logger/FileLogger.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Logging; +using System; +using System.IO; + +namespace TodoApiDTO.Logger +{ + public class FileLogger : ILogger + { + private string _path; + private static object _lock = new object(); + + public FileLogger(string path) + { + _path = path; + } + + public IDisposable BeginScope(TState state) + { + return null; + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel == LogLevel.Error; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (IsEnabled(logLevel) && formatter != null) + { + lock (_lock) + { + File.AppendAllText(_path, formatter(state, exception) + Environment.NewLine); + } + } + } + } +} diff --git a/Logger/FileLoggerExtensions.cs b/Logger/FileLoggerExtensions.cs new file mode 100644 index 00000000..586d4192 --- /dev/null +++ b/Logger/FileLoggerExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; + +namespace TodoApiDTO.Logger +{ + public static class FileLoggerExtensions + { + public static ILoggerFactory AddFile(this ILoggerFactory factory, + string filePath) + { + factory.AddProvider(new FileLoggerProvider(filePath)); + return factory; + } + } +} diff --git a/Logger/FileLoggerProvider.cs b/Logger/FileLoggerProvider.cs new file mode 100644 index 00000000..0bb86fa5 --- /dev/null +++ b/Logger/FileLoggerProvider.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Logging; + +namespace TodoApiDTO.Logger +{ + public class FileLoggerProvider : ILoggerProvider + { + private string _path; + public FileLoggerProvider(string path) + { + _path = path; + } + + public ILogger CreateLogger(string categoryName) + { + return new FileLogger(_path); + } + + public void Dispose() + { + } + } +} diff --git a/Models/TodoItem.cs b/Models/TodoItem.cs index 1f6e5465..21cae71a 100644 --- a/Models/TodoItem.cs +++ b/Models/TodoItem.cs @@ -1,6 +1,5 @@ -namespace TodoApi.Models +namespace TodoApiDTO.Models { - #region snippet public class TodoItem { public long Id { get; set; } @@ -8,5 +7,4 @@ public class TodoItem public bool IsComplete { get; set; } public string Secret { get; set; } } - #endregion } \ No newline at end of file diff --git a/Repo/ITodoRepository.cs b/Repo/ITodoRepository.cs new file mode 100644 index 00000000..d6e4f103 --- /dev/null +++ b/Repo/ITodoRepository.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using TodoApiDTO.Models; + +namespace TodoApiDTO.Repositories +{ + public interface ITodoRepository + { + Task> GetTodoItemsAsync(); + Task GetTodoItemAsync(long id); + void CreateTodoItem(TodoItem todoItem); + void DeleteTodoItem(TodoItem todoItem); + Task SaveChangesAsync(); + bool TodoItemExists(long id); + } +} \ No newline at end of file diff --git a/Repo/TodoRepository.cs b/Repo/TodoRepository.cs new file mode 100644 index 00000000..87a469b9 --- /dev/null +++ b/Repo/TodoRepository.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using TodoApiDTO.Models; +using TodoApiDTO.Data; + +namespace TodoApiDTO.Repositories +{ + public class TodoRepository : ITodoRepository + { + private readonly TodoContext _context; + + public TodoRepository(TodoContext context) + { + _context = context; + } + + public async Task> GetTodoItemsAsync() + { + return (IEnumerable)await _context.TodoItems.ToListAsync(); + } + + public async Task GetTodoItemAsync(long id) + { + return await _context.TodoItems.FindAsync(id); + } + + public void CreateTodoItem(TodoItem todoItem) + { + _context.TodoItems.Add(todoItem); + } + + public void DeleteTodoItem(TodoItem todoItem) + { + _context.TodoItems.Remove(todoItem); + } + + public async Task SaveChangesAsync() + { + await _context.SaveChangesAsync(); + } + + public bool TodoItemExists(long id) => + _context.TodoItems.Any(e => e.Id == id); + } +} diff --git a/Startup.cs b/Startup.cs index bbfbc83d..107d8219 100644 --- a/Startup.cs +++ b/Startup.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; @@ -11,7 +12,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using TodoApi.Models; +using Microsoft.OpenApi.Models; +using TodoApiDTO.Data; +using TodoApiDTO.Logger; +using TodoApiDTO.Repositories; namespace TodoApi { @@ -27,17 +31,37 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + //services.AddDbContext(opt => + // opt.UseInMemoryDatabase("TodoList")); + services.AddDbContext(opt => - opt.UseInMemoryDatabase("TodoList")); + opt.UseSqlServer(Configuration.GetConnectionString("TodoList"))); + + services.AddScoped(); + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todo API", Version = "v1" }); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { + loggerFactory.AddFile(Path.Combine(Directory.GetCurrentDirectory(), "logs", "logger.txt")); + if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo API v1"); + + // To serve SwaggerUI at application's root page, set the RoutePrefix property to an empty string. + c.RoutePrefix = string.Empty; + }); } app.UseHttpsRedirection(); @@ -52,4 +76,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) }); } } -} +} \ No newline at end of file diff --git a/appsettings.Development.json b/appsettings.Development.json index 8983e0fc..e3551c66 100644 --- a/appsettings.Development.json +++ b/appsettings.Development.json @@ -5,5 +5,8 @@ "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } + }, + "ConnectionStrings": { + "TodoList": "Server=(localdb)\\mssqllocaldb;Database=TodoList;Trusted_Connection=True;" } }