Skip to content

Commit 24c69bf

Browse files
committed
Refactor LessonController and add LessonService
Refactored LessonController to use ILessonService, centralizing lesson-related logic in the new LessonService for improved modularity and maintainability. Added caching with CacheSettings to enhance performance and reduce database load. Updated Program.cs to register new dependencies and services. Improved error handling and reorganized code for better readability. Added placeholders for unimplemented methods in LessonService.
1 parent b7f24d7 commit 24c69bf

6 files changed

Lines changed: 182 additions & 39 deletions

File tree

Configurations/CacheSettings.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace AskAMuslimAPI.Configurations
2+
{
3+
public class CacheSettings
4+
{
5+
public int LessonCacheMinutes { get; set; }
6+
public int CourseCacheMinutes { get; set; }
7+
}
8+
}

Controllers/LessonController.cs

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
using AutoMapper;
1+
using AskAMuslimAPI.Services.Interfaces;
2+
using AutoMapper;
23
using EdufyAPI.DTOs.LessonDTOs;
34
using EdufyAPI.Helpers;
4-
using EdufyAPI.Models;
55
using EdufyAPI.Repository.Interfaces;
66
using Microsoft.AspNetCore.Mvc;
77

@@ -11,43 +11,41 @@ namespace EdufyAPI.Controllers
1111
[ApiController]
1212
public class LessonController : ControllerBase
1313
{
14+
private readonly ILessonService _lessonService;
1415
private readonly IUnitOfWork _unitOfWork;
1516
private readonly IMapper _mapper;
1617
private readonly string ThumbnailsFolderName = "lesson-thumbnails";
1718
private readonly string VideosFolderName = "lesson-videos";
1819

19-
public LessonController(IUnitOfWork unitOfWork, IMapper mapper)
20+
public LessonController(IUnitOfWork unitOfWork, IMapper mapper, ILessonService lessonService)
2021
{
2122
_unitOfWork = unitOfWork;
2223
_mapper = mapper;
24+
_lessonService = lessonService;
25+
}
26+
27+
/// <summary>
28+
/// Creates a new lesson.
29+
/// </summary>
30+
/// <param name="createLessonDto">The lesson data.</param>
31+
/// <returns>The created lesson.</returns>
32+
[HttpPost]
33+
public async Task<ActionResult<LessonReadDTO>> CreateLesson(LessonCreateDTO createLessonDto)
34+
{
35+
if (!ModelState.IsValid) return BadRequest(ModelState);
36+
var lesson = _lessonService.CreateLesson(createLessonDto);
37+
return CreatedAtAction(nameof(GetLessonByID), new { id = lesson.Id }, lesson);
2338
}
2439

2540
/// <summary>
2641
/// Retrieves all lessons.
2742
/// </summary>
2843
/// <returns>A list of lessons with their details.</returns>
2944
[HttpGet]
45+
[ProducesResponseType(typeof(IEnumerable<LessonReadDTO>), StatusCodes.Status200OK)]
3046
public async Task<ActionResult<IEnumerable<LessonReadDTO>>> GetAllLessons()
3147
{
32-
var lessons = await _unitOfWork.LessonRepository.GetAllAsync();
33-
if (!lessons.Any())
34-
{
35-
return Ok(Enumerable.Empty<LessonReadDTO>());
36-
}
37-
38-
var lessonDtos = _mapper.Map<IEnumerable<LessonReadDTO>>(lessons);
39-
foreach (var lessonDto in lessonDtos)
40-
{
41-
if (!string.IsNullOrEmpty(lessonDto.ThumbnailUrl))
42-
{
43-
lessonDto.ThumbnailUrl = ConstructFileUrlHelper.ConstructFileUrl(Request, ThumbnailsFolderName, lessonDto.ThumbnailUrl);
44-
}
45-
if (!string.IsNullOrEmpty(lessonDto.VideoUrl))
46-
{
47-
lessonDto.VideoUrl = ConstructFileUrlHelper.ConstructFileUrl(Request, VideosFolderName, lessonDto.VideoUrl);
48-
}
49-
}
50-
return Ok(lessonDtos);
48+
return await _lessonService.GetAllLessons();
5149
}
5250

5351
/// <summary>
@@ -102,23 +100,7 @@ public async Task<ActionResult<IEnumerable<LessonReadDTO>>> GetCourseLessons(str
102100
return Ok(lessonDtos);
103101
}
104102

105-
/// <summary>
106-
/// Creates a new lesson.
107-
/// </summary>
108-
/// <param name="createLessonDto">The lesson data.</param>
109-
/// <returns>The created lesson.</returns>
110-
[HttpPost]
111-
public async Task<ActionResult<LessonReadDTO>> CreateLesson(LessonCreateDTO createLessonDto)
112-
{
113-
if (!ModelState.IsValid) return BadRequest(ModelState);
114-
115-
var lesson = _mapper.Map<Lesson>(createLessonDto);
116-
await _unitOfWork.LessonRepository.AddAsync(lesson);
117-
await _unitOfWork.SaveChangesAsync();
118103

119-
var lessonReadDto = _mapper.Map<LessonReadDTO>(lesson);
120-
return CreatedAtAction(nameof(GetLessonByID), new { id = lesson.Id }, lessonReadDto);
121-
}
122104

123105
/// <summary>
124106
/// Updates an existing lesson.

Program.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using EdufyAPI.DTOs;
1+
using AskAMuslimAPI.Configurations;
2+
using AskAMuslimAPI.Services;
3+
using AskAMuslimAPI.Services.Interfaces;
4+
using EdufyAPI.DTOs;
25
using EdufyAPI.Helpers;
36
using EdufyAPI.Models;
47
using EdufyAPI.Models.Roles;
@@ -118,6 +121,9 @@
118121
#endregion
119122

120123

124+
125+
builder.Services.Configure<CacheSettings>(builder.Configuration.GetSection("CacheSettings"));
126+
121127
#region Register services for dependency injection
122128
// 🔹 Register UnitOfWork and Generic Repository for dependency injection
123129

@@ -138,6 +144,7 @@
138144
builder.Services.AddScoped<IQuestionService, QuestionService>();
139145
builder.Services.AddScoped<IOptionService, OptionService>();
140146
builder.Services.AddScoped<IAnswerService, AnswerService>();
147+
builder.Services.AddScoped<ILessonService, LessonService>();
141148
#endregion
142149

143150

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using EdufyAPI.DTOs.LessonDTOs;
2+
using Microsoft.AspNetCore.Mvc;
3+
4+
namespace AskAMuslimAPI.Services.Interfaces
5+
{
6+
public interface ILessonService
7+
{
8+
Task<ActionResult<LessonReadDTO>> CreateLesson(LessonCreateDTO createLessonDto);
9+
Task<ActionResult<IEnumerable<LessonReadDTO>>> GetAllLessons();
10+
Task<ActionResult<LessonReadDTO>> GetLessonByID(string id);
11+
12+
Task<ActionResult<IEnumerable<LessonReadDTO>>> GetCourseLessons(string courseId);
13+
14+
Task<IActionResult> UpdateLesson(string id, LessonUpdateDTO updateLessonDto);
15+
16+
Task<IActionResult> DeleteLesson(string id);
17+
}
18+
}

Services/LessonService.cs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using AskAMuslimAPI.Configurations;
2+
using AskAMuslimAPI.Services.Interfaces;
3+
using AutoMapper;
4+
using EdufyAPI.DTOs.LessonDTOs;
5+
using EdufyAPI.Helpers;
6+
using EdufyAPI.Models;
7+
using EdufyAPI.Repository.Interfaces;
8+
using EdufyAPI.Services.Interfaces;
9+
using Microsoft.AspNetCore.Mvc;
10+
using Microsoft.Extensions.Options;
11+
12+
namespace AskAMuslimAPI.Services
13+
{
14+
public class LessonService(
15+
ICacheService cacheService,
16+
IUnitOfWork unitOfWork,
17+
IMapper mapper,
18+
IOptions<CacheSettings> cacheSettings,
19+
IHttpContextAccessor httpContextAccessor) : ILessonService
20+
{
21+
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
22+
private readonly ICacheService _cacheService = cacheService;
23+
private readonly IUnitOfWork _unitOfWork = unitOfWork;
24+
private readonly IMapper _mapper = mapper;
25+
private readonly string ThumbnailsFolderName = "lesson-thumbnails";
26+
private readonly string VideosFolderName = "lesson-videos";
27+
private readonly CacheSettings _cacheSettings = cacheSettings.Value;
28+
29+
public async Task<ActionResult<LessonReadDTO>> CreateLesson(LessonCreateDTO createLessonDto)
30+
{
31+
if (string.IsNullOrEmpty(createLessonDto.CourseId))
32+
return new BadRequestObjectResult("Course ID is required.");
33+
34+
try
35+
{
36+
var lesson = _mapper.Map<Lesson>(createLessonDto);
37+
await _unitOfWork.LessonRepository.AddAsync(lesson);
38+
await _unitOfWork.SaveChangesAsync();
39+
40+
var lessonReadDto = _mapper.Map<LessonReadDTO>(lesson);
41+
42+
// Invalidate cache for the course, and re-fetch the lessons
43+
await _cacheService.RemoveDataAsync($"CourseLessons_{createLessonDto.CourseId}");
44+
await _cacheService.RemoveDataAsync("AllLessons");
45+
46+
return lessonReadDto;
47+
}
48+
catch (Exception ex)
49+
{
50+
return new BadRequestObjectResult($"Error retrieving course: {ex.Message}");
51+
}
52+
}
53+
54+
public async Task<ActionResult<IEnumerable<LessonReadDTO>>> GetAllLessons()
55+
{
56+
var lessons = await _unitOfWork.LessonRepository.GetAllAsync();
57+
if (!lessons.Any())
58+
{
59+
return new List<LessonReadDTO>();
60+
}
61+
62+
try
63+
{
64+
// Check if lessons are cached
65+
var cachedLessons = await _cacheService.GetDataAsync<IEnumerable<LessonReadDTO>>("AllLessons");
66+
if (cachedLessons != null)
67+
{
68+
return new ActionResult<IEnumerable<LessonReadDTO>>(cachedLessons);
69+
}
70+
71+
var lessonDtos = _mapper.Map<IEnumerable<LessonReadDTO>>(lessons);
72+
var request = _httpContextAccessor.HttpContext?.Request;
73+
74+
foreach (var lessonDto in lessonDtos)
75+
{
76+
77+
if (!string.IsNullOrEmpty(lessonDto.ThumbnailUrl))
78+
{
79+
lessonDto.ThumbnailUrl = ConstructFileUrlHelper.ConstructFileUrl(request, ThumbnailsFolderName, lessonDto.ThumbnailUrl);
80+
}
81+
82+
if (!string.IsNullOrEmpty(lessonDto.VideoUrl))
83+
{
84+
lessonDto.VideoUrl = ConstructFileUrlHelper.ConstructFileUrl(request, VideosFolderName, lessonDto.VideoUrl);
85+
}
86+
87+
await _cacheService.SetDataAsync($"Lesson_{lessonDto.Id}", lessonDto, DateTimeOffset.Now.AddMinutes(_cacheSettings.LessonCacheMinutes));
88+
await _cacheService.SetDataAsync($"CourseLessons_{lessonDto.CourseId}", lessonDtos, DateTimeOffset.Now.AddMinutes(_cacheSettings.LessonCacheMinutes));
89+
}
90+
91+
// Cache the lessons
92+
await _cacheService.SetDataAsync("AllLessons", lessonDtos, DateTimeOffset.Now.AddMinutes(_cacheSettings.LessonCacheMinutes));
93+
94+
return new ActionResult<IEnumerable<LessonReadDTO>>(lessonDtos);
95+
}
96+
catch (Exception ex)
97+
{
98+
return new BadRequestObjectResult($"Error retrieving lessons: {ex.Message}");
99+
}
100+
101+
}
102+
103+
public Task<ActionResult<IEnumerable<LessonReadDTO>>> GetCourseLessons(string courseId)
104+
{
105+
throw new NotImplementedException();
106+
}
107+
108+
public Task<ActionResult<LessonReadDTO>> GetLessonByID(string id)
109+
{
110+
throw new NotImplementedException();
111+
}
112+
public Task<IActionResult> UpdateLesson(string id, LessonUpdateDTO updateLessonDto)
113+
{
114+
throw new NotImplementedException();
115+
}
116+
public Task<IActionResult> DeleteLesson(string id)
117+
{
118+
throw new NotImplementedException();
119+
}
120+
121+
122+
}
123+
}

appsettings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
"Issuer": "TemplateAPI",
2020
"Audience": "TemplateUsers",
2121
"TokenExpirationDays": 60
22+
},
23+
24+
"CacheSettings": {
25+
"LessonCacheMinutes": 15,
26+
"CourseCacheMinutes": 15
2227
}
2328

2429
// 🔹 CORS Allowed Origins (Optional)

0 commit comments

Comments
 (0)