From a435a66440da935739a5f03f1b0bf36b227ac5c4 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Mon, 21 Jul 2025 23:41:07 +0530 Subject: [PATCH 01/14] adding redis --- .../infrastructure/nt.helper/Constants.cs | 9 +++++++++ .../infrastructure/nt.orchestrator.AppHost/Program.cs | 9 ++++++++- .../nt.orchestrator.AppHost.csproj | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/server/nt.microservice/infrastructure/nt.helper/Constants.cs b/server/nt.microservice/infrastructure/nt.helper/Constants.cs index 03ac84ae..6d2c350c 100644 --- a/server/nt.microservice/infrastructure/nt.helper/Constants.cs +++ b/server/nt.microservice/infrastructure/nt.helper/Constants.cs @@ -140,6 +140,15 @@ public static class ReviewService { public const string ServiceName = "nt-reviewservice-service"; + + public static class Cache + { + public const string InstanceName = "nt-reviewservice-cache"; + public const string ContainerName = "nt.reviewservice.cache"; + public const string UserNameKey = $"{ServiceName}-UserName"; + public const string PasswordKey = $"{ServiceName}-Password"; + } + public static class Database { public const string InstanceName = "nt-reviewservice-db"; diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs index 25daa839..91e34d4e 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs @@ -66,6 +66,12 @@ .WithDataVolume() .WithMongoExpress(); +var redisReview = builder.AddRedis(Constants.ReviewService.Cache.InstanceName, 6379) + //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisHostKey, "host.docker.internal") + //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisPortKey, "6379") + .WithContainerName(Constants.ReviewService.Cache.ContainerName) + .WithHttpEndpoint(port: 8081, targetPort: 8081, isProxied: true); + var blobStorage = builder.AddContainer("nt-userservice-blobstorage", infrastructureSettings.BlobStorage.DockerImage) .WithVolume("//d/Source/nt/server/nt.microservice/services/UserService/BlobStorage:/data") .WithArgs("azurite-blob", "--blobHost", "0.0.0.0", "-l", "/data") @@ -180,7 +186,8 @@ .WithEnvironment(Constants.Global.EnvironmentVariables.RunningWithVariable, Constants.Global.EnvironmentVariables.RunningWithValue) .WithUrls(c => c.Urls.ForEach(u => u.DisplayText = $"Open API ({u.Endpoint?.EndpointName})")) .WithReference(mongoDbReview) - .WaitFor(mongoDbReview); + .WaitFor(mongoDbReview) + .WaitFor(redisReview); var gateway = builder.AddProject(Constants.Gateway.ServiceName, launchProfileName: Constants.Gateway.LaunchProfile) diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj index 5e65dca2..5e5b10c3 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj @@ -17,6 +17,7 @@ + From 90174a43a026f9039b5d7fa0d4bd5c7fd798f338 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Tue, 22 Jul 2025 21:32:46 +0530 Subject: [PATCH 02/14] added Redis Commander and Insight --- .../infrastructure/nt.orchestrator.AppHost/Program.cs | 4 +++- .../nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs index 91e34d4e..834e9457 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs @@ -70,7 +70,9 @@ //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisHostKey, "host.docker.internal") //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisPortKey, "6379") .WithContainerName(Constants.ReviewService.Cache.ContainerName) - .WithHttpEndpoint(port: 8081, targetPort: 8081, isProxied: true); + .WithHttpEndpoint(port: 8081, targetPort: 8081, isProxied: true) + .WithRedisInsight() + .WithRedisCommander(); var blobStorage = builder.AddContainer("nt-userservice-blobstorage", infrastructureSettings.BlobStorage.DockerImage) .WithVolume("//d/Source/nt/server/nt.microservice/services/UserService/BlobStorage:/data") diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj index 5e5b10c3..35af664b 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/nt.orchestrator.AppHost.csproj @@ -19,6 +19,7 @@ + From 414e9ba58cc0d065d0c32dc0609d556b898d8054 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Tue, 22 Jul 2025 21:45:58 +0530 Subject: [PATCH 03/14] adding client --- .../Options/CacheOptions.cs | 9 +++++++++ .../ReviewService.Presentation.Api/Program.cs | 14 ++++++++++++-- .../ReviewService.Presenation.Api.csproj | 1 + .../appsettings.json | 6 ++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Options/CacheOptions.cs diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Options/CacheOptions.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Options/CacheOptions.cs new file mode 100644 index 00000000..05eadc10 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Options/CacheOptions.cs @@ -0,0 +1,9 @@ +namespace ReviewService.Presenation.Api.Options; + +public record CacheOptions +{ + public string? CacheName { get; init; } = "ReviewServiceCache"; // Default cache name + public int ExpirationInMinutes { get; init; } = 60; // Default expiration time in minutes + public bool EnableCaching { get; init; } = true; // Flag to enable or disable caching + public string? ConnectionString { get; init; } = null; // Optional connection string for distributed cache +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs index 7d69f589..fd7baabc 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.Options; using ReviewService.Presenation.Api; using ReviewService.Presenation.Api.Helpers; using ReviewService.Presenation.Api.Options; +using StackExchange.Redis; namespace ReviewService.Api; @@ -11,14 +13,22 @@ public static async Task Main(string[] args) var builder = WebApplication.CreateBuilder(args); builder.AddServiceDefaults(); builder.Services.Configure(builder.Configuration.GetSection(nameof(DatabaseOptions))); - + builder.Services.Configure(builder.Configuration.GetSection(nameof(CacheOptions))); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); builder.Services.RegisterServices(); - + builder.Services.AddSingleton(sp => + { + var cacheOptions = sp.GetRequiredService>().Value; + if (cacheOptions.ConnectionString is null) + { + throw new InvalidOperationException("Redis connection string is not configured."); + } + return ConnectionMultiplexer.Connect(cacheOptions.ConnectionString); + }); var app = builder.Build(); app.MapDefaultEndpoints(); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj index 8f686d5a..6366d5b0 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj @@ -11,6 +11,7 @@ + diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json index aa4a93c6..5f3f3a44 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json @@ -11,5 +11,11 @@ "ConnectionString": "mongodb://root:mypass@nt.reviewservice.db:27018/?authSource=admin", "DatabaseName": "ntreviewstore", "ReviewCollectionName": "reviews" + }, + "CacheOptions": { + "ConnectionString": "redis://nt.reviewservice.cache:6379", + "InstanceName": "nt-reviewservice-cache", + "ExpirationInMinutes": 60, + "EnableCaching": true } } From a8c9f876a3ddabb85d75d9d5bc9363008e4108e1 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Tue, 22 Jul 2025 22:20:57 +0530 Subject: [PATCH 04/14] adding caching service --- .../Services/ICachingService.cs | 7 +++++++ .../ReviewService.Application.Services.csproj | 1 + .../Services/CachingService.cs | 20 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs new file mode 100644 index 00000000..2d4591e7 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs @@ -0,0 +1,7 @@ +namespace ReviewService.Application.Interfaces.Services; + +public interface ICachingService +{ + void Set(string key, T value, TimeSpan expirationTime) where T : class; + T? Get(string key) where T : class; +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/ReviewService.Application.Services.csproj b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/ReviewService.Application.Services.csproj index 57630032..c300ab49 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/ReviewService.Application.Services.csproj +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/ReviewService.Application.Services.csproj @@ -7,6 +7,7 @@ + diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs new file mode 100644 index 00000000..fd264313 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -0,0 +1,20 @@ +using ReviewService.Application.Interfaces.Services; +using StackExchange.Redis; +using System.Text.Json; + +namespace ReviewService.Application.Services.Services; + +public class CachingService(IConnectionMultiplexer connectionMultiplexer) : ICachingService +{ + public IConnectionMultiplexer ConnectionMultiplexer => connectionMultiplexer; + public IDatabase Database => ConnectionMultiplexer.GetDatabase(); + public void Set(string key, T value, TimeSpan expirationTime) where T : class + { + Database.StringSet(key, JsonSerializer.Serialize(value), expirationTime); + } + public T? Get(string key) where T : class + { + var value = Database.StringGet(key); + return value.IsNullOrEmpty ? null : JsonSerializer.Deserialize(value!); + } +} From 6edce6ef0ea9ecb06d678cf33b6fa91562447386 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Tue, 22 Jul 2025 22:42:34 +0530 Subject: [PATCH 05/14] working on caching --- .../Services/ICachingService.cs | 4 +-- .../Operations/ReviewService.cs | 28 +++++++++++++++++-- .../Services/CachingService.cs | 8 +++--- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs index 2d4591e7..b35ace01 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs @@ -2,6 +2,6 @@ public interface ICachingService { - void Set(string key, T value, TimeSpan expirationTime) where T : class; - T? Get(string key) where T : class; + Task SetAsync(string key, T value, TimeSpan expirationTime) where T : class; + Task GetAsync(string key) where T : class; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index f6e5af83..9c918832 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using ReviewService.Application.DTO.Reviews; using ReviewService.Application.Interfaces.Operations; +using ReviewService.Application.Interfaces.Services; using ReviewService.Domain.Entities; using ReviewService.Domain.Repositories; @@ -12,11 +13,13 @@ public class ReviewService : IReviewService private readonly IReviewRepository _reviewRepository; private readonly IMapper _mapper; private readonly ILogger _logger; - public ReviewService(IReviewRepository reviewRepository,IMapper mapper, ILogger logger) + private readonly ICachingService _cachingService; + public ReviewService(IReviewRepository reviewRepository,ICachingService cachingService, IMapper mapper, ILogger logger) { _reviewRepository = reviewRepository ?? throw new ArgumentNullException(nameof(reviewRepository)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + _cachingService = cachingService ?? throw new ArgumentNullException(nameof(cachingService)); } public Task> GetReviewsByMovieIdAsync(Guid movieId) { @@ -41,8 +44,27 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera { try { - var results = await _reviewRepository.GetRecentReviewsForUsersAsync(userIds, count); - return _mapper.Map, IEnumerable>(results); + var results = new List(); + var nonCachedUsers = new List(); + + foreach(var id in userIds) + { + var cacheKey = $"user:{id}:recentReviews"; + var cachedReviews = await _cachingService.GetAsync>(cacheKey).ConfigureAwait(false); + + if (cachedReviews != null && cachedReviews.Any()) + { + results.AddRange(cachedReviews); + } + else + { + nonCachedUsers.Add(id); + } + + } + var fromDbResults = await _reviewRepository.GetRecentReviewsForUsersAsync(nonCachedUsers, count); + results.AddRange(_mapper.Map, IEnumerable>(fromDbResults)); + return results; } catch (Exception) { diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs index fd264313..8b5df817 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -8,13 +8,13 @@ public class CachingService(IConnectionMultiplexer connectionMultiplexer) : ICac { public IConnectionMultiplexer ConnectionMultiplexer => connectionMultiplexer; public IDatabase Database => ConnectionMultiplexer.GetDatabase(); - public void Set(string key, T value, TimeSpan expirationTime) where T : class + public async Task SetAsync(string key, T value, TimeSpan expirationTime) where T : class { - Database.StringSet(key, JsonSerializer.Serialize(value), expirationTime); + await Database.StringSetAsync(key, JsonSerializer.Serialize(value), expirationTime); } - public T? Get(string key) where T : class + public async Task GetAsync(string key) where T : class { - var value = Database.StringGet(key); + var value = await Database.StringGetAsync(key); return value.IsNullOrEmpty ? null : JsonSerializer.Deserialize(value!); } } From a2259a24db014e2354bcf173513faadab0f4d030 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Tue, 22 Jul 2025 22:44:37 +0530 Subject: [PATCH 06/14] logs errr --- .../Operations/ReviewService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index 9c918832..c1d5f9c6 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -66,9 +66,9 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera results.AddRange(_mapper.Map, IEnumerable>(fromDbResults)); return results; } - catch (Exception) + catch (Exception ex) { - + _logger.LogError(ex, "An error occurred while creating a review."); throw; } } From 46e4e1f2a4bb15d5a158d8ce49a5be3299f31333 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Wed, 23 Jul 2025 06:09:52 +0530 Subject: [PATCH 07/14] working on caching added IOC registration --- .../Services/IReviewCachingService.cs | 8 +++++++ .../Operations/ReviewService.cs | 14 +++++++++-- .../Services/CachingService.cs | 3 ++- .../Services/ReviewCachingService.cs | 24 +++++++++++++++++++ .../Helpers/IServiceCollectionExtension.cs | 21 ++++++++++++++-- .../ReviewService.Presentation.Api/Program.cs | 9 ------- .../ReviewService.Presenation.Api.csproj | 1 + 7 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs new file mode 100644 index 00000000..52c694f5 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs @@ -0,0 +1,8 @@ +using ReviewService.Application.DTO.Reviews; + +namespace ReviewService.Application.Interfaces.Services; + +public interface IReviewCachingService +{ + Task SaveInCache(ReviewDto review); +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index c1d5f9c6..53844516 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -62,8 +62,18 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera } } - var fromDbResults = await _reviewRepository.GetRecentReviewsForUsersAsync(nonCachedUsers, count); - results.AddRange(_mapper.Map, IEnumerable>(fromDbResults)); + var dbResults = await _reviewRepository.GetRecentReviewsForUsersAsync(nonCachedUsers, count); + + foreach (var review in dbResults) + { + var cacheKey = $"user:{review.Author}:recentReviews"; + var reviewDto = _mapper.Map(review); + + // Cache the review for future requests + await _cachingService.SetAsync(cacheKey, reviewDto, TimeSpan.FromMinutes(5)).ConfigureAwait(false); + } + + results.AddRange(_mapper.Map, IEnumerable>(dbResults)); return results; } catch (Exception ex) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs index 8b5df817..05713b52 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -1,4 +1,5 @@ -using ReviewService.Application.Interfaces.Services; +using Microsoft.Extensions.Options; +using ReviewService.Application.Interfaces.Services; using StackExchange.Redis; using System.Text.Json; diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs new file mode 100644 index 00000000..66efdd6d --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs @@ -0,0 +1,24 @@ +using AutoMapper; +using ReviewService.Application.DTO.Reviews; +using ReviewService.Application.Interfaces.Services; +using ReviewService.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReviewService.Application.Services.Services; + +public class ReviewCachingService(ICachingService cachingService, IMapper mapper) : IReviewCachingService +{ + protected ICachingService CachingService => cachingService; + protected IMapper Mapper => mapper; + public async Task SaveInCache(ReviewDto review) + { + var cacheKey = $"user:{review.UserName}:recentReviews"; + + // Cache the review for future requests + await CachingService.SetAsync(cacheKey, review, TimeSpan.FromMinutes(5)).ConfigureAwait(false); + } +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs index c4b1d9cb..72df5bef 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs @@ -1,4 +1,8 @@ -using ReviewService.Application.Interfaces.Operations; +using Microsoft.Extensions.Options; +using ReviewService.Application.Interfaces.Operations; +using ReviewService.Application.Interfaces.Services; +using ReviewService.Presenation.Api.Options; +using StackExchange.Redis; namespace ReviewService.Presenation.Api.Helpers; @@ -8,7 +12,11 @@ public static void RegisterServices(this IServiceCollection serviceCollection) { // Register your services here // Example: serviceCollection.AddSingleton(); - + serviceCollection.AddScoped(); + + // Generic Services + serviceCollection.AddScoped(); + // Register initializers and providers RegisterInitializersAndProviders(serviceCollection); } @@ -16,6 +24,15 @@ private static void RegisterInitializersAndProviders(IServiceCollection serviceC { // Add any initializers or providers needed for the application // Example: serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(sp => + { + var cacheOptions = sp.GetRequiredService>().Value; + if (cacheOptions.ConnectionString is null) + { + throw new InvalidOperationException("Redis connection string is not configured."); + } + return ConnectionMultiplexer.Connect(cacheOptions.ConnectionString); + }); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs index fd7baabc..09414581 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs @@ -20,15 +20,6 @@ public static async Task Main(string[] args) // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); builder.Services.RegisterServices(); - builder.Services.AddSingleton(sp => - { - var cacheOptions = sp.GetRequiredService>().Value; - if (cacheOptions.ConnectionString is null) - { - throw new InvalidOperationException("Redis connection string is not configured."); - } - return ConnectionMultiplexer.Connect(cacheOptions.ConnectionString); - }); var app = builder.Build(); app.MapDefaultEndpoints(); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj index 6366d5b0..3aca4620 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj @@ -24,6 +24,7 @@ + From 1adc4b7026e745852ba8a33c18013a2e305f4f60 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Wed, 23 Jul 2025 20:43:28 +0530 Subject: [PATCH 08/14] completing read recent reviews --- .../Reviews/ReviewDto.cs | 3 +- .../Services/ICachingService.cs | 7 +++- .../Services/IReviewCachingService.cs | 2 + .../Extensions/DateTimeExtensions.cs | 23 +++++++++++ .../Operations/ReviewService.cs | 14 ++++--- .../Services/CachingService.cs | 24 +++++++++-- .../Services/ReviewCachingService.cs | 41 +++++++++++++++---- .../Controllers/UserReviewsController.cs | 2 +- .../Helpers/IServiceCollectionExtension.cs | 7 +++- 9 files changed, 100 insertions(+), 23 deletions(-) create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Extensions/DateTimeExtensions.cs diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs index 8f5c2e43..960679e5 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs @@ -7,6 +7,7 @@ public class ReviewDto public string Content { get; set; } = string.Empty; public int Rating { get; set; } - public string UserName { get; set; } = string.Empty; + public string Author { get; set; } = string.Empty; + public DateTime CreatedOn { get; set; } = DateTime.UtcNow; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs index b35ace01..ac84ef42 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/ICachingService.cs @@ -2,6 +2,9 @@ public interface ICachingService { - Task SetAsync(string key, T value, TimeSpan expirationTime) where T : class; - Task GetAsync(string key) where T : class; + TimeSpan ExpirationTime { get; } + Task StringSetAsync(string key, T value) where T : class; + Task SortedSetAsync(string key, T value, double score) where T : class; + Task StringGetAsync(string key) where T : class; + Task> SortedSetRangeByScoreAsync(string key, int count) where T : class; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs index 52c694f5..78e662ce 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Services/IReviewCachingService.cs @@ -5,4 +5,6 @@ namespace ReviewService.Application.Interfaces.Services; public interface IReviewCachingService { Task SaveInCache(ReviewDto review); + Task ReadCache(Guid reviewId); + Task> ReadUserRecentReviews(string userName, int count = 10); } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Extensions/DateTimeExtensions.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Extensions/DateTimeExtensions.cs new file mode 100644 index 00000000..e9d494c1 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Extensions/DateTimeExtensions.cs @@ -0,0 +1,23 @@ +namespace ReviewService.Application.Services.Extensions; + +public static class DateTimeExtensions +{ + /// + /// Converts a DateTime to a Unix timestamp. + /// + /// The DateTime to convert. + /// The Unix timestamp as a long. + public static long ToUnixTimestamp(this DateTime dateTime) + { + return new DateTimeOffset(dateTime).ToUnixTimeSeconds(); + } + /// + /// Converts a Unix timestamp to a DateTime. + /// + /// The Unix timestamp to convert. + /// The DateTime representation of the Unix timestamp. + public static DateTime FromUnixTimestamp(long unixTimestamp) + { + return DateTimeOffset.FromUnixTimeSeconds(unixTimestamp).UtcDateTime; + } +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index 53844516..4d2f8398 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -3,6 +3,7 @@ using ReviewService.Application.DTO.Reviews; using ReviewService.Application.Interfaces.Operations; using ReviewService.Application.Interfaces.Services; +using ReviewService.Application.Services.Extensions; using ReviewService.Domain.Entities; using ReviewService.Domain.Repositories; @@ -13,13 +14,13 @@ public class ReviewService : IReviewService private readonly IReviewRepository _reviewRepository; private readonly IMapper _mapper; private readonly ILogger _logger; - private readonly ICachingService _cachingService; - public ReviewService(IReviewRepository reviewRepository,ICachingService cachingService, IMapper mapper, ILogger logger) + private readonly IReviewCachingService _reviewCachingService; + public ReviewService(IReviewRepository reviewRepository,IReviewCachingService cachingService, IMapper mapper, ILogger logger) { _reviewRepository = reviewRepository ?? throw new ArgumentNullException(nameof(reviewRepository)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _cachingService = cachingService ?? throw new ArgumentNullException(nameof(cachingService)); + _reviewCachingService = cachingService ?? throw new ArgumentNullException(nameof(cachingService)); } public Task> GetReviewsByMovieIdAsync(Guid movieId) { @@ -50,7 +51,7 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera foreach(var id in userIds) { var cacheKey = $"user:{id}:recentReviews"; - var cachedReviews = await _cachingService.GetAsync>(cacheKey).ConfigureAwait(false); + var cachedReviews = await _reviewCachingService.ReadUserRecentReviews(id.ToString(),3).ConfigureAwait(false); if (cachedReviews != null && cachedReviews.Any()) { @@ -70,11 +71,12 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera var reviewDto = _mapper.Map(review); // Cache the review for future requests - await _cachingService.SetAsync(cacheKey, reviewDto, TimeSpan.FromMinutes(5)).ConfigureAwait(false); + await _reviewCachingService.SaveInCache(reviewDto).ConfigureAwait(false); } results.AddRange(_mapper.Map, IEnumerable>(dbResults)); - return results; + + return results.OrderByDescending(x=>x.CreatedOn); } catch (Exception ex) { diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs index 05713b52..ef70b00e 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -5,17 +5,33 @@ namespace ReviewService.Application.Services.Services; -public class CachingService(IConnectionMultiplexer connectionMultiplexer) : ICachingService +public class CachingService(IConnectionMultiplexer connectionMultiplexer, int timeOutInMinutes) : ICachingService { public IConnectionMultiplexer ConnectionMultiplexer => connectionMultiplexer; public IDatabase Database => ConnectionMultiplexer.GetDatabase(); - public async Task SetAsync(string key, T value, TimeSpan expirationTime) where T : class + + public TimeSpan ExpirationTime { get; } = TimeSpan.FromMinutes(timeOutInMinutes); + + public async Task StringSetAsync(string key, T value) where T : class + { + await Database.StringSetAsync(key, JsonSerializer.Serialize(value), ExpirationTime); + } + + public async Task SortedSetAsync(string key, T value, double score) where T : class { - await Database.StringSetAsync(key, JsonSerializer.Serialize(value), expirationTime); + var serializedValue = JsonSerializer.Serialize(value); + await Database.SortedSetAddAsync(key, serializedValue, score); //DateTimeOffset.UtcNow.ToUnixTimeSeconds()); + await Database.KeyExpireAsync(key, ExpirationTime); } - public async Task GetAsync(string key) where T : class + public async Task StringGetAsync(string key) where T : class { var value = await Database.StringGetAsync(key); return value.IsNullOrEmpty ? null : JsonSerializer.Deserialize(value!); } + + public async Task> SortedSetRangeByScoreAsync(string key, int count) where T : class + { + var values = await Database.SortedSetRangeByScoreAsync(key, order: Order.Descending, take: count); + return values.Select(value => JsonSerializer.Deserialize(value!)).Where(value => value != null)!; + } } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs index 66efdd6d..59f51657 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs @@ -1,12 +1,7 @@ using AutoMapper; using ReviewService.Application.DTO.Reviews; using ReviewService.Application.Interfaces.Services; -using ReviewService.Domain.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using ReviewService.Application.Services.Extensions; namespace ReviewService.Application.Services.Services; @@ -14,11 +9,41 @@ public class ReviewCachingService(ICachingService cachingService, IMapper mapper { protected ICachingService CachingService => cachingService; protected IMapper Mapper => mapper; + public async Task SaveInCache(ReviewDto review) { - var cacheKey = $"user:{review.UserName}:recentReviews"; + var reviewCacheKey = GenerateReviewCacheKey(review.Id); + var sortedCacheKey = GenerateUserRecentReviewsCacheKey(review.Author); // Cache the review for future requests - await CachingService.SetAsync(cacheKey, review, TimeSpan.FromMinutes(5)).ConfigureAwait(false); + await CachingService.StringSetAsync(reviewCacheKey, review).ConfigureAwait(false); + await CachingService.SortedSetAsync(sortedCacheKey, review, review.CreatedOn.ToUnixTimestamp()).ConfigureAwait(false); + } + + public async Task ReadCache(Guid reviewId) + { + var reviewCacheKey = GenerateReviewCacheKey(reviewId); + return await CachingService.StringGetAsync(reviewCacheKey).ConfigureAwait(false); + } + + public async Task> ReadUserRecentReviews(string userName, int count = 10) + { + var sortedCacheKey = GenerateUserRecentReviewsCacheKey(userName); + var reviewKeys = await CachingService.SortedSetRangeByScoreAsync(sortedCacheKey, count).ConfigureAwait(false); + var reviews = new List(); + foreach (var reviewKey in reviewKeys) + { + var review = await CachingService.StringGetAsync(reviewKey).ConfigureAwait(false); + if (review != null) + { + reviews.Add(review); + } + } + + return reviews.OrderByDescending(r => r.CreatedOn).ToList(); } + + private static string GenerateReviewCacheKey(Guid id) => $"review:{id}"; + + private static string GenerateUserRecentReviewsCacheKey(string userName) => $"user:{userName}:recentReviews"; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs index 422f131b..1e6d3501 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs @@ -52,7 +52,7 @@ public async Task GetRecentReviewsForUsers(Get MovieId = r.MovieId, Content = r.Content, Rating = r.Rating, - UserName = r.UserName, + UserName = r.Author, }).ToList() }; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs index 72df5bef..e9326be5 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs @@ -15,7 +15,12 @@ public static void RegisterServices(this IServiceCollection serviceCollection) serviceCollection.AddScoped(); // Generic Services - serviceCollection.AddScoped(); + serviceCollection.AddScoped(sp => + { + var connectionMultiplexer = sp.GetRequiredService(); + var cacheOptions = sp.GetRequiredService>().Value; + return new ReviewService.Application.Services.Services.CachingService(connectionMultiplexer, cacheOptions.ExpirationInMinutes); + }); // Register initializers and providers RegisterInitializersAndProviders(serviceCollection); From b4be1f3fdeeb672f4c531ea836809e57f794cde2 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Wed, 23 Jul 2025 22:00:36 +0530 Subject: [PATCH 09/14] adding missing service registrations --- .../Seed/MalayalamReviewsSeed.cs | 26 +++++++------- .../Helpers/IServiceCollectionExtension.cs | 35 ++++++++++++++++++- .../ReviewService.Presentation.Api/Program.cs | 8 +++-- .../ReviewService.Presenation.Api.csproj | 1 + 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Seed/MalayalamReviewsSeed.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Seed/MalayalamReviewsSeed.cs index c2ad9435..5c9738ef 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Seed/MalayalamReviewsSeed.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Seed/MalayalamReviewsSeed.cs @@ -11,7 +11,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("6191e634-14c8-45d1-898f-191060cdbec1"), Rating = 5, Title = "Ustad Hotel Feels", - Author = "moviebuff_91" + Author = "jia.anu" }, new() { Content = "Dulquer and Thilakan make this movie a beautiful emotional ride. Music was perfect.", @@ -19,7 +19,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("6191e634-14c8-45d1-898f-191060cdbec1"), Rating = 4, Title = "Beautiful Blend", - Author = "cinemalover" + Author = "jia.anu" }, new() { Content = "Joji is an intense, slow burn thriller. Brilliant acting by Fahadh as always.", @@ -27,7 +27,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("bea50cb7-41b6-4a35-b77f-358e8c43f850"), Rating = 4, Title = "Dark and Gripping", - Author = "screenaddict" + Author = "naina.anu" }, new() { Content = "Minimal dialogues, maximum impact. Joji is a masterclass in modern Malayalam cinema.", @@ -35,7 +35,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("bea50cb7-41b6-4a35-b77f-358e8c43f850"), Rating = 5, Title = "Minimalist Brilliance", - Author = "cinecritic" + Author = "jia.anu" }, new() { Content = "A hilarious ride with unexpected twists. Oru Vadakkan Selfie is pure fun.", @@ -43,7 +43,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("bd4b80a0-1516-4b71-887b-714c52459f23"), Rating = 4, Title = "Comedy Hit", - Author = "ajay.m" + Author = "naina.anu" }, new() { Content = "Selfie gone wrong but in the best way possible. Loved the storytelling and humor.", @@ -51,7 +51,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("bd4b80a0-1516-4b71-887b-714c52459f23"), Rating = 5, Title = "Smart Comedy", - Author = "techjunkie" + Author = "jia.anu" }, new() { Content = "Malik is powerful. A political saga with gripping performances. Fahadh nailed it!", @@ -59,7 +59,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("0003be11-f19a-4b9c-a1b2-b7e195b53d3e"), Rating = 5, Title = "Political Power", - Author = "seriouscinema" + Author = "naina.anu" }, new() { Content = "One of the best performances by Fahadh. Malik stays with you long after it ends.", @@ -67,7 +67,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("0003be11-f19a-4b9c-a1b2-b7e195b53d3e"), Rating = 5, Title = "Brilliant Execution", - Author = "rajfilm" + Author = "jia.anu" }, new() { Content = "Premam is nostalgic, fun, and full of charm. Every phase of love was shown beautifully.", @@ -75,7 +75,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("af3e9bed-3e04-4f06-856d-3c572605bf4d"), Rating = 5, Title = "Love Story Goals", - Author = "dreamy_eyes" + Author = "naina.anu" }, new() { Content = "Great music, wonderful performances. Premam is a modern Malayalam classic.", @@ -83,7 +83,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("af3e9bed-3e04-4f06-856d-3c572605bf4d"), Rating = 5, Title = "Evergreen", - Author = "anu_reviews" + Author = "jia.anu" }, new() { Content = "Jana Gana Mana is thought-provoking. Raises valid questions about justice and media.", @@ -91,7 +91,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("47435a1d-6b24-4cfc-b3a6-0be543322187"), Rating = 4, Title = "Relevant and Bold", - Author = "truthseeker" + Author = "naina.anu" }, new() { Content = "Powerful script and solid performances. Worth watching more than once.", @@ -99,7 +99,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("47435a1d-6b24-4cfc-b3a6-0be543322187"), Rating = 5, Title = "Must Watch", - Author = "prithvi_rules" + Author = "jia.anu" }, new() { Content = "Ustad Hotel remains a comfort movie. It warms the soul and stirs the appetite.", @@ -107,7 +107,7 @@ public static class MalayalamReviewsSeed MovieId = Guid.Parse("6191e634-14c8-45d1-898f-191060cdbec1"), Rating = 5, Title = "Feel-Good Watch", - Author = "greenchili" + Author = "naina.anu" }, new() { Content = "Malik's political layers and gripping visuals make it a standout Malayalam film.", diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs index e9326be5..0a46115d 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Options; +using MongoDB.Driver; using ReviewService.Application.Interfaces.Operations; using ReviewService.Application.Interfaces.Services; using ReviewService.Presenation.Api.Options; @@ -10,21 +11,53 @@ public static class IServiceCollectionExtension { public static void RegisterServices(this IServiceCollection serviceCollection) { + + // Register your services here // Example: serviceCollection.AddSingleton(); + serviceCollection.AddAutoMapper(typeof(IServiceCollectionExtension)); + + + serviceCollection.AddSingleton(sp => + { + var dbOptions = sp.GetRequiredService(); + var connectionString = dbOptions.ConnectionString; + return new MongoClient(connectionString); + }); + + serviceCollection.AddSingleton(sp => + { + var dbOptions = sp.GetRequiredService(); + var client = sp.GetRequiredService(); + var databaseName = dbOptions.DatabaseName; + return client.GetDatabase(databaseName); + }); + + + RegisterRepositories(serviceCollection); + // User Services serviceCollection.AddScoped(); // Generic Services - serviceCollection.AddScoped(sp => + serviceCollection.AddSingleton(sp => { var connectionMultiplexer = sp.GetRequiredService(); var cacheOptions = sp.GetRequiredService>().Value; return new ReviewService.Application.Services.Services.CachingService(connectionMultiplexer, cacheOptions.ExpirationInMinutes); }); + serviceCollection.AddSingleton(); + // Register initializers and providers RegisterInitializersAndProviders(serviceCollection); } + + private static void RegisterRepositories(IServiceCollection serviceCollection) + { + // Register your repositories here + // Example: serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + } private static void RegisterInitializersAndProviders(IServiceCollection serviceCollection) { // Add any initializers or providers needed for the application diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs index 09414581..12f79df4 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Program.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Options; +using MongoDB.Driver; using ReviewService.Presenation.Api; using ReviewService.Presenation.Api.Helpers; using ReviewService.Presenation.Api.Options; @@ -14,12 +15,14 @@ public static async Task Main(string[] args) builder.AddServiceDefaults(); builder.Services.Configure(builder.Configuration.GetSection(nameof(DatabaseOptions))); builder.Services.Configure(builder.Configuration.GetSection(nameof(CacheOptions))); - + // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); builder.Services.RegisterServices(); + builder.Services.AddEndpointsApiExplorer(); // for minimal APIs + builder.Services.AddSwaggerGen(); // generates the Swagger JSON var app = builder.Build(); app.MapDefaultEndpoints(); @@ -37,7 +40,8 @@ public static async Task Main(string[] args) // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.MapOpenApi(); + app.UseSwagger(); // serves /swagger/v1/swagger.json + app.UseSwaggerUI(); // serves /swagger } app.UseHttpsRedirection(); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj index 3aca4620..b7af8bd9 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/ReviewService.Presenation.Api.csproj @@ -18,6 +18,7 @@ + From 627778e612c67388c4f110a4c13c67adb5c9b165 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Thu, 24 Jul 2025 11:17:46 +0530 Subject: [PATCH 10/14] trying to connect to redis --- .../nt.orchestrator.AppHost/Program.cs | 4 ++-- .../Operations/IReviewService.cs | 2 +- .../Queries/GetRecentReviewsForUsersQuery.cs | 2 +- .../Operations/ReviewService.cs | 6 +++--- .../Repositories/IReviewRepository.cs | 2 +- .../Repositories/ReviewRepository.cs | 2 +- .../Controllers/UserReviewsController.cs | 5 +++-- .../Helpers/IServiceCollectionExtension.cs | 17 ++++++++++++----- .../Models/CreateReviewRequest.cs | 2 +- .../appsettings.json | 2 +- 10 files changed, 26 insertions(+), 18 deletions(-) diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs index 834e9457..1c1a2c07 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs @@ -67,8 +67,8 @@ .WithMongoExpress(); var redisReview = builder.AddRedis(Constants.ReviewService.Cache.InstanceName, 6379) - //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisHostKey, "host.docker.internal") - //.WithEnvironment(Constants.ReviewService.EnvironmentVariable.RedisPortKey, "6379") + .WithEnvironment("Redis__Host", Constants.ReviewService.Cache.InstanceName) // Set in your app + .WithEnvironment("Redis__Port", "6379") .WithContainerName(Constants.ReviewService.Cache.ContainerName) .WithHttpEndpoint(port: 8081, targetPort: 8081, isProxied: true) .WithRedisInsight() diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Operations/IReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Operations/IReviewService.cs index 067fcdee..3e5cdc9f 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Operations/IReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Interfaces/Operations/IReviewService.cs @@ -4,7 +4,7 @@ namespace ReviewService.Application.Interfaces.Operations; public interface IReviewService { - Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); + Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); Task> GetReviewsByMovieIdAsync(Guid movieId); Task CreateReviewAsync(ReviewDto reviewDto); } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Orchestration/Queries/GetRecentReviewsForUsersQuery.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Orchestration/Queries/GetRecentReviewsForUsersQuery.cs index ea9b6c04..87919a92 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Orchestration/Queries/GetRecentReviewsForUsersQuery.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Orchestration/Queries/GetRecentReviewsForUsersQuery.cs @@ -5,6 +5,6 @@ namespace ReviewService.Application.Orchestration.Queries; public class GetRecentReviewsForUsersQuery : IRequest> { - public IEnumerable UserIds { get; set; } = []; + public IEnumerable UserIds { get; set; } = []; public int Count { get; set; } } \ No newline at end of file diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index 4d2f8398..eb5a8c9c 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -41,17 +41,17 @@ public async Task CreateReviewAsync(ReviewDto reviewDto) } } - public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3) + public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3) { try { var results = new List(); - var nonCachedUsers = new List(); + var nonCachedUsers = new List(); foreach(var id in userIds) { var cacheKey = $"user:{id}:recentReviews"; - var cachedReviews = await _reviewCachingService.ReadUserRecentReviews(id.ToString(),3).ConfigureAwait(false); + var cachedReviews = await _reviewCachingService.ReadUserRecentReviews(id,3).ConfigureAwait(false); if (cachedReviews != null && cachedReviews.Any()) { diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs index b4be932f..7b46099c 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs @@ -8,5 +8,5 @@ public interface IReviewRepository : IGenericRepository Task> GetReviewsByUserIdAsync(Guid userId); Task> GetReviewsByRatingAsync(int rating); Task> GetReviewsByDateRangeAsync(DateTime startDate, DateTime endDate); - Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); + Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); } \ No newline at end of file diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs index 5add2101..31918c18 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs @@ -8,7 +8,7 @@ namespace ReviewService.Infrastructure.Repository.Repositories; public class ReviewRepository(IMongoDatabase mongoDatabase,IMapper mapper) : GenericRepository(mongoDatabase,mapper, "Reviews"), IReviewRepository { - public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 10) + public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 10) { var filter = Builders.Filter.In(r => r.Author, userIds.Select(u => u.ToString())); var result = await Collection diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs index 1e6d3501..8e332f95 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Controllers/UserReviewsController.cs @@ -29,8 +29,9 @@ public ActionResult GetReviewsForMovie(Guid movieId) return default!; } - - public async Task GetRecentReviewsForUsers(GetRecentReviewsForUsersRequest request) + [HttpPost] + [Route(@"GetRecentReviewsForUsers")] + public async Task GetRecentReviewsForUsers([FromBody]GetRecentReviewsForUsersRequest request) { try { diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs index 0a46115d..7c3953c3 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs @@ -1,7 +1,9 @@ -using Microsoft.Extensions.Options; +using MediatR; +using Microsoft.Extensions.Options; using MongoDB.Driver; using ReviewService.Application.Interfaces.Operations; using ReviewService.Application.Interfaces.Services; +using ReviewService.Domain.Entities; using ReviewService.Presenation.Api.Options; using StackExchange.Redis; @@ -16,18 +18,18 @@ public static void RegisterServices(this IServiceCollection serviceCollection) // Register your services here // Example: serviceCollection.AddSingleton(); serviceCollection.AddAutoMapper(typeof(IServiceCollectionExtension)); - + serviceCollection.AddMediatR(typeof(ReviewService.Application.Orchestration.Commands.CreateReviewCommand).Assembly); serviceCollection.AddSingleton(sp => { - var dbOptions = sp.GetRequiredService(); + var dbOptions = sp.GetRequiredService>().Value; var connectionString = dbOptions.ConnectionString; return new MongoClient(connectionString); }); serviceCollection.AddSingleton(sp => { - var dbOptions = sp.GetRequiredService(); + var dbOptions = sp.GetRequiredService>().Value; var client = sp.GetRequiredService(); var databaseName = dbOptions.DatabaseName; return client.GetDatabase(databaseName); @@ -69,7 +71,12 @@ private static void RegisterInitializersAndProviders(IServiceCollection serviceC { throw new InvalidOperationException("Redis connection string is not configured."); } - return ConnectionMultiplexer.Connect(cacheOptions.ConnectionString); + + var options = ConfigurationOptions.Parse(cacheOptions.ConnectionString); + options.AbortOnConnectFail = false; + options.ConnectRetry = 5; + options.ConnectTimeout = 10000; + return ConnectionMultiplexer.Connect(options); }); serviceCollection.AddSingleton(); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs index 4aeb03fc..0c64d928 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs @@ -8,7 +8,7 @@ public record CreateReviewRequest public string UserName { get; set; } = string.Empty; } -public record GetRecentReviewsForUsersRequest(IEnumerable UserIds, int Count = 3); +public record GetRecentReviewsForUsersRequest(IEnumerable UserIds, int Count = 3); public record GetRecentReviewsForUsersResponse { diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json index 5f3f3a44..9a4519bb 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json @@ -13,7 +13,7 @@ "ReviewCollectionName": "reviews" }, "CacheOptions": { - "ConnectionString": "redis://nt.reviewservice.cache:6379", + "ConnectionString": "nt.reviewservice.cache:6379,abortConnect=false", "InstanceName": "nt-reviewservice-cache", "ExpirationInMinutes": 60, "EnableCaching": true From 392fa3aa9ae6a99cd969a3e9e0ddc3162991f72a Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Thu, 24 Jul 2025 19:46:43 +0530 Subject: [PATCH 11/14] set password for cache --- .../infrastructure/nt.orchestrator.AppHost/Program.cs | 6 ++++-- .../Services/CachingService.cs | 1 + .../Helpers/IServiceCollectionExtension.cs | 1 + .../ReviewService.Presentation.Api/appsettings.json | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs index 1c1a2c07..82692a89 100644 --- a/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs +++ b/server/nt.microservice/infrastructure/nt.orchestrator.AppHost/Program.cs @@ -66,7 +66,8 @@ .WithDataVolume() .WithMongoExpress(); -var redisReview = builder.AddRedis(Constants.ReviewService.Cache.InstanceName, 6379) +var redisPassword = builder.AddParameter(Constants.ReviewService.Cache.PasswordKey, "Password123", secret: true); +var redisReview = builder.AddRedis(Constants.ReviewService.Cache.InstanceName, port: 6379, password: redisPassword) .WithEnvironment("Redis__Host", Constants.ReviewService.Cache.InstanceName) // Set in your app .WithEnvironment("Redis__Port", "6379") .WithContainerName(Constants.ReviewService.Cache.ContainerName) @@ -189,7 +190,8 @@ .WithUrls(c => c.Urls.ForEach(u => u.DisplayText = $"Open API ({u.Endpoint?.EndpointName})")) .WithReference(mongoDbReview) .WaitFor(mongoDbReview) - .WaitFor(redisReview); + .WaitFor(redisReview) + .WithReference(redisReview); var gateway = builder.AddProject(Constants.Gateway.ServiceName, launchProfileName: Constants.Gateway.LaunchProfile) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs index ef70b00e..8902194b 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -31,6 +31,7 @@ public async Task SortedSetAsync(string key, T value, double score) where T : public async Task> SortedSetRangeByScoreAsync(string key, int count) where T : class { + var g = Database.Ping(); var values = await Database.SortedSetRangeByScoreAsync(key, order: Order.Descending, take: count); return values.Select(value => JsonSerializer.Deserialize(value!)).Where(value => value != null)!; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs index 7c3953c3..587dff07 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Helpers/IServiceCollectionExtension.cs @@ -76,6 +76,7 @@ private static void RegisterInitializersAndProviders(IServiceCollection serviceC options.AbortOnConnectFail = false; options.ConnectRetry = 5; options.ConnectTimeout = 10000; + options.SyncTimeout = 10000; return ConnectionMultiplexer.Connect(options); }); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json index 9a4519bb..454469a6 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/appsettings.json @@ -13,7 +13,7 @@ "ReviewCollectionName": "reviews" }, "CacheOptions": { - "ConnectionString": "nt.reviewservice.cache:6379,abortConnect=false", + "ConnectionString": "localhost:6379,password=Password123,abortConnect=false", "InstanceName": "nt-reviewservice-cache", "ExpirationInMinutes": 60, "EnableCaching": true From e3c8de8a393ff13b5338d8fb7073715514f5b474 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Fri, 25 Jul 2025 00:43:52 +0530 Subject: [PATCH 12/14] corrected collection name --- .../Documents/ReviewDocument.cs | 16 +++++++++------- .../Repositories/ReviewRepository.cs | 8 ++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs index 0add6001..0c32fc16 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs @@ -1,4 +1,5 @@ -using MongoDB.Entities; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Entities; namespace ReviewService.Infrastructure.Repository.Documents; @@ -8,22 +9,23 @@ public class ReviewDocument:Entity internal const string CollectionName = "reviews"; - [Field("movieId")] + [BsonElement("movieId")] public Guid MovieId { get; set; } - [Field("title")] + [BsonElement("title")] public string Title { get; set; } = string.Empty; - [Field("content")] + [BsonElement("content")] public string Content { get; set; } = string.Empty; - [Field("rating")] + [BsonElement("rating")] + public int Rating { get; set; } - [Field("author")] + [BsonElement("author")] public string Author { get; set; } = string.Empty; - [Field("createdOn")] + [BsonElement("createdOn")] public DateTime CreatedOn { get; set; } = DateTime.UtcNow; } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs index 31918c18..05dbbfef 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs @@ -6,15 +6,15 @@ namespace ReviewService.Infrastructure.Repository.Repositories; -public class ReviewRepository(IMongoDatabase mongoDatabase,IMapper mapper) : GenericRepository(mongoDatabase,mapper, "Reviews"), IReviewRepository +public class ReviewRepository(IMongoDatabase mongoDatabase,IMapper mapper) : GenericRepository(mongoDatabase,mapper, ReviewDocument.CollectionName), IReviewRepository { public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 10) { var filter = Builders.Filter.In(r => r.Author, userIds.Select(u => u.ToString())); - var result = await Collection + var query = Collection .Find(filter) - .SortByDescending(r => r.CreatedOn) - .Limit(count).ToCursorAsync().ConfigureAwait(false); + .SortByDescending(r => r.CreatedOn).Limit(count); + var result = await query.ToCursorAsync().ConfigureAwait(false); return Mapper.Map>(result.ToEnumerable()) ?? throw new InvalidOperationException("No recent reviews found for the specified users."); } From fc58be88930f7f46140fd931ea57b0a721bdea25 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Fri, 25 Jul 2025 21:07:20 +0530 Subject: [PATCH 13/14] Fixed mapping --- .../Reviews/ReviewDto.cs | 3 ++ .../Operations/ReviewService.cs | 6 ++-- .../Entities/{Review.cs => ReviewEntity.cs} | 2 +- .../Repositories/IReviewRepository.cs | 12 ++++---- .../Documents/ReviewDocument.cs | 4 +++ .../Repositories/ReviewRepository.cs | 22 +++++++------- .../Maps/ProfileMap.cs | 30 +++++++++++++++++++ .../Models/CreateReviewRequest.cs | 16 ---------- .../Models/GetRecentReviewsForUsersRequest.cs | 3 ++ .../GetRecentReviewsForUsersResponse.cs | 16 ++++++++++ 10 files changed, 77 insertions(+), 37 deletions(-) rename server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/{Review.cs => ReviewEntity.cs} (93%) create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Maps/ProfileMap.cs create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersRequest.cs create mode 100644 server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersResponse.cs diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs index 960679e5..a3bc5e3a 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.DTO/Reviews/ReviewDto.cs @@ -10,4 +10,7 @@ public class ReviewDto public string Author { get; set; } = string.Empty; public DateTime CreatedOn { get; set; } = DateTime.UtcNow; + public IEnumerable UpvotedBy { get; set; } = []; + public IEnumerable DownvotedBy { get; set; } = []; + } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs index eb5a8c9c..f250eaa0 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Operations/ReviewService.cs @@ -31,7 +31,7 @@ public async Task CreateReviewAsync(ReviewDto reviewDto) { try { - var review = await _reviewRepository.AddAsync(_mapper.Map(reviewDto)).ConfigureAwait(false); + var review = await _reviewRepository.AddAsync(_mapper.Map(reviewDto)).ConfigureAwait(false); return review.Id; } catch (Exception ex) @@ -68,13 +68,13 @@ public async Task> GetRecentReviewsForUsersAsync(IEnumera foreach (var review in dbResults) { var cacheKey = $"user:{review.Author}:recentReviews"; - var reviewDto = _mapper.Map(review); + var reviewDto = _mapper.Map(review); // Cache the review for future requests await _reviewCachingService.SaveInCache(reviewDto).ConfigureAwait(false); } - results.AddRange(_mapper.Map, IEnumerable>(dbResults)); + results.AddRange(_mapper.Map, IEnumerable>(dbResults)); return results.OrderByDescending(x=>x.CreatedOn); } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/Review.cs b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/ReviewEntity.cs similarity index 93% rename from server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/Review.cs rename to server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/ReviewEntity.cs index 189c50d2..da27038d 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/Review.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Entities/ReviewEntity.cs @@ -1,5 +1,5 @@ namespace ReviewService.Domain.Entities; -public class Review : IEntity +public class ReviewEntity : IEntity { public Guid Id { get; set; } public string MovieId { get; set; } = null!; diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs index 7b46099c..e757f3c0 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Domain/Repositories/IReviewRepository.cs @@ -2,11 +2,11 @@ namespace ReviewService.Domain.Repositories; -public interface IReviewRepository : IGenericRepository +public interface IReviewRepository : IGenericRepository { - Task> GetReviewsByMovieIdAsync(Guid movieId); - Task> GetReviewsByUserIdAsync(Guid userId); - Task> GetReviewsByRatingAsync(int rating); - Task> GetReviewsByDateRangeAsync(DateTime startDate, DateTime endDate); - Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); + Task> GetReviewsByMovieIdAsync(Guid movieId); + Task> GetReviewsByUserIdAsync(Guid userId); + Task> GetReviewsByRatingAsync(int rating); + Task> GetReviewsByDateRangeAsync(DateTime startDate, DateTime endDate); + Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 3); } \ No newline at end of file diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs index 0c32fc16..1e5a2fa1 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Documents/ReviewDocument.cs @@ -28,4 +28,8 @@ public class ReviewDocument:Entity [BsonElement("createdOn")] public DateTime CreatedOn { get; set; } = DateTime.UtcNow; + public IEnumerable UpvotedBy { get; set; } = []; + + public IEnumerable DownvotedBy { get; set; } = []; + } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs index 05dbbfef..4ca7d54d 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Infrastructure.Repository/Repositories/ReviewRepository.cs @@ -6,19 +6,19 @@ namespace ReviewService.Infrastructure.Repository.Repositories; -public class ReviewRepository(IMongoDatabase mongoDatabase,IMapper mapper) : GenericRepository(mongoDatabase,mapper, ReviewDocument.CollectionName), IReviewRepository +public class ReviewRepository(IMongoDatabase mongoDatabase,IMapper mapper) : GenericRepository(mongoDatabase,mapper, ReviewDocument.CollectionName), IReviewRepository { - public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 10) + public async Task> GetRecentReviewsForUsersAsync(IEnumerable userIds, int count = 10) { var filter = Builders.Filter.In(r => r.Author, userIds.Select(u => u.ToString())); var query = Collection .Find(filter) .SortByDescending(r => r.CreatedOn).Limit(count); var result = await query.ToCursorAsync().ConfigureAwait(false); - return Mapper.Map>(result.ToEnumerable()) ?? throw new InvalidOperationException("No recent reviews found for the specified users."); + return Mapper.Map>(result.ToEnumerable()) ?? throw new InvalidOperationException("No recent reviews found for the specified users."); } - public async Task> GetReviewsByDateRangeAsync(DateTime startDate, DateTime endDate) + public async Task> GetReviewsByDateRangeAsync(DateTime startDate, DateTime endDate) { var filter = Builders.Filter.And( @@ -36,20 +36,20 @@ public async Task> GetReviewsByDateRangeAsync(DateTime start .ToCursorAsync() .ConfigureAwait(false); - return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found in the specified date range.")); + return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found in the specified date range.")); } - public async Task> GetReviewsByMovieIdAsync(Guid movieId) + public async Task> GetReviewsByMovieIdAsync(Guid movieId) { var filter = Builders.Filter.Eq(r => r.MovieId, movieId); var result = await Collection .Find(filter) .ToCursorAsync().ConfigureAwait(false); - return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found for the specified movie.")); + return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found for the specified movie.")); } - public async Task> GetReviewsByRatingAsync(int rating) + public async Task> GetReviewsByRatingAsync(int rating) { var filter = Builders.Filter.Eq(r => r.Rating, rating); var result = await Collection @@ -57,10 +57,10 @@ public async Task> GetReviewsByRatingAsync(int rating) .ToCursorAsync() .ConfigureAwait(false); - return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found with the specified rating.")); + return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found with the specified rating.")); } - public async Task> GetReviewsByUserIdAsync(Guid userId) + public async Task> GetReviewsByUserIdAsync(Guid userId) { var filter = Builders.Filter.Eq(r => r.Author, userId.ToString()); var result = await Collection @@ -68,6 +68,6 @@ public async Task> GetReviewsByUserIdAsync(Guid userId) .ToCursorAsync() .ConfigureAwait(false); - return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found for the specified user.")); + return Mapper.Map>(result.ToEnumerable() ?? throw new InvalidOperationException("No reviews found for the specified user.")); } } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Maps/ProfileMap.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Maps/ProfileMap.cs new file mode 100644 index 00000000..e1a6310d --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Maps/ProfileMap.cs @@ -0,0 +1,30 @@ +using AutoMapper; + +namespace ReviewService.Presenation.Api.Maps; + +public class ProfileMap:Profile +{ + public ProfileMap() + { + // Define your mappings here + // Example: CreateMap(); + + CreateMap() + .ForMember(dest => dest.ID, opt => opt.MapFrom(src => src.Id.ToString())) + .ForMember(dest => dest.MovieId, opt => opt.MapFrom(src => src.MovieId.ToString())) + .ForMember(dest => dest.CreatedOn, opt => opt.MapFrom(src => src.CreatedOn)) + .ForMember(dest => dest.UpvotedBy, opt => opt.MapFrom(src => src.UpvotedBy.Select(u => u.ToString()))) + .ForMember(dest => dest.DownvotedBy, opt => opt.MapFrom(src => src.DownvotedBy.Select(u => u.ToString()))) + .ReverseMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => Guid.Parse(src.ID))); + + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) + .ForMember(dest => dest.MovieId, opt => opt.MapFrom(src => src.MovieId.ToString())) + .ForMember(dest => dest.CreatedOn, opt => opt.MapFrom(src => src.CreatedOn)) + .ForMember(dest => dest.UpvotedBy, opt => opt.MapFrom(src => src.UpvotedBy.Select(u => u.ToString()))) + .ForMember(dest => dest.DownvotedBy, opt => opt.MapFrom(src => src.DownvotedBy.Select(u => u.ToString()))) + .ReverseMap(); + + } +} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs index 0c64d928..e39ec21e 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/CreateReviewRequest.cs @@ -8,21 +8,5 @@ public record CreateReviewRequest public string UserName { get; set; } = string.Empty; } -public record GetRecentReviewsForUsersRequest(IEnumerable UserIds, int Count = 3); - -public record GetRecentReviewsForUsersResponse -{ - public IEnumerable Reviews { get; init; } = []; -}; -public record GetRcentReviewsForUserReviewItem -{ - public Guid ReviewId { get; init; } - public Guid MovieId { get; init; } - public string MovieTitle { get; init; } = string.Empty; - public string Content { get; init; } = string.Empty; - public int Rating { get; init; } - public string UserName { get; init; } = string.Empty; - public string UserDisplayName { get; set; } = string.Empty; -} diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersRequest.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersRequest.cs new file mode 100644 index 00000000..3957c672 --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersRequest.cs @@ -0,0 +1,3 @@ +namespace ReviewService.Presenation.Api.Models; + +public record GetRecentReviewsForUsersRequest(IEnumerable UserIds, int Count = 3); diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersResponse.cs b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersResponse.cs new file mode 100644 index 00000000..a495400a --- /dev/null +++ b/server/nt.microservice/services/ReviewService/ReviewService.Presentation.Api/Models/GetRecentReviewsForUsersResponse.cs @@ -0,0 +1,16 @@ +namespace ReviewService.Presenation.Api.Models; + +public record GetRecentReviewsForUsersResponse +{ + public IEnumerable Reviews { get; init; } = []; +}; + +public record GetRcentReviewsForUserReviewItem +{ + public Guid ReviewId { get; init; } + public Guid MovieId { get; init; } + public string MovieTitle { get; init; } = string.Empty; + public string Content { get; init; } = string.Empty; + public int Rating { get; init; } + public string UserName { get; init; } = string.Empty; +} From 8b7d3005784260156ba1fa4625881c093696e259 Mon Sep 17 00:00:00 2001 From: Anu Viswan Date: Sat, 26 Jul 2025 08:28:23 +0530 Subject: [PATCH 14/14] Fixed key for cache service --- .../Services/CachingService.cs | 9 +++++++-- .../Services/ReviewCachingService.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs index 8902194b..9211d7a5 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/CachingService.cs @@ -31,8 +31,13 @@ public async Task SortedSetAsync(string key, T value, double score) where T : public async Task> SortedSetRangeByScoreAsync(string key, int count) where T : class { - var g = Database.Ping(); var values = await Database.SortedSetRangeByScoreAsync(key, order: Order.Descending, take: count); - return values.Select(value => JsonSerializer.Deserialize(value!)).Where(value => value != null)!; + return values.Select(value => JsonSerializer.Deserialize(value!.ToString())).Where(value => value != null)!; + } + + public async Task> SortedSetRangeByScoreAsync(string key, int count) + { + var values = await Database.SortedSetRangeByScoreAsync(key, order: Order.Descending, take: count); + return values.Select(value => value.ToString()!); } } diff --git a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs index 59f51657..ac3b00a8 100644 --- a/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs +++ b/server/nt.microservice/services/ReviewService/ReviewService.Application.Services/Services/ReviewCachingService.cs @@ -17,7 +17,7 @@ public async Task SaveInCache(ReviewDto review) // Cache the review for future requests await CachingService.StringSetAsync(reviewCacheKey, review).ConfigureAwait(false); - await CachingService.SortedSetAsync(sortedCacheKey, review, review.CreatedOn.ToUnixTimestamp()).ConfigureAwait(false); + await CachingService.SortedSetAsync(sortedCacheKey, reviewCacheKey, review.CreatedOn.ToUnixTimestamp()).ConfigureAwait(false); } public async Task ReadCache(Guid reviewId)