From 6bc45e44863c9eb5089ec2e2f6c91a12574858b0 Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Fri, 26 Dec 2025 06:07:24 +0330 Subject: [PATCH] feat(bff): Add App Version management endpoints - Add appversion.proto with GetAll, Get, Update RPCs - Add GetAllAppVersionsQuery and handler - Add UpdateAppVersionCommand and handler - Add AppVersionService gRPC service - Add AppVersionProfile for Mapster mappings - Update IApplicationContractContext with AppVersions client - Bump Configuration.Protobuf to 1.0.20 --- .../Interfaces/IApplicationContractContext.cs | 4 + .../UpdateAppVersionCommand.cs | 39 ++++++++ .../UpdateAppVersionCommandHandler.cs | 39 ++++++++ .../GetAllAppVersionsQuery.cs | 6 ++ .../GetAllAppVersionsQueryHandler.cs | 40 +++++++++ .../GetAllAppVersionsResponseDto.cs | 20 +++++ .../BackOffice.BFF.Domain.csproj | 2 +- .../Services/ApplicationContractContext.cs | 4 + .../Common/Mappings/AppVersionProfile.cs | 42 +++++++++ .../Services/AppVersionService.cs | 55 ++++++++++++ ...ckOffice.BFF.Configuration.Protobuf.csproj | 3 +- .../Protos/appversion.proto | 88 +++++++++++++++++++ 12 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommand.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommandHandler.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQuery.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQueryHandler.cs create mode 100644 src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsResponseDto.cs create mode 100644 src/BackOffice.BFF.WebApi/Common/Mappings/AppVersionProfile.cs create mode 100644 src/BackOffice.BFF.WebApi/Services/AppVersionService.cs create mode 100644 src/Protobufs/BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto diff --git a/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs b/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs index dbccf13..0d3b29c 100644 --- a/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs +++ b/src/BackOffice.BFF.Application/Common/Interfaces/IApplicationContractContext.cs @@ -23,6 +23,7 @@ using CMSMicroservice.Protobuf.Protos.Configuration; using CMSMicroservice.Protobuf.Protos.DiscountOrder; using CMSMicroservice.Protobuf.Protos.ManualPayment; using CMSMicroservice.Protobuf.Protos.NetworkMembership; +using CMSMicroservice.Protobuf.Protos.AppVersion; namespace BackOffice.BFF.Application.Common.Interfaces; @@ -65,6 +66,9 @@ public interface IApplicationContractContext // Manual Payments (Admin) - BackOffice BFF gRPC ManualPaymentContract.ManualPaymentContractClient ManualPayments { get; } + + // App Version Management + AppVersionContract.AppVersionContractClient AppVersions { get; } #endregion } diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommand.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommand.cs new file mode 100644 index 0000000..012c910 --- /dev/null +++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommand.cs @@ -0,0 +1,39 @@ +namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion; + +public class UpdateAppVersionCommand : IRequest +{ + /// + /// نام اپلیکیشن (FrontOffice, BackOffice, Mobile) + /// + public string AppName { get; set; } = string.Empty; + + /// + /// ورژن فعلی + /// + public string CurrentVersion { get; set; } = string.Empty; + + /// + /// حداقل ورژن مورد نیاز + /// + public string? MinRequiredVersion { get; set; } + + /// + /// آیا نیاز به پاک کردن کامل کش دارد؟ + /// + public bool RequiresFullCacheClear { get; set; } + + /// + /// پیام آپدیت برای کاربران + /// + public string? UpdateMessage { get; set; } + + /// + /// یادداشت‌های نسخه + /// + public string? ReleaseNotes { get; set; } + + /// + /// دلیل آپدیت (برای لاگ ادمین) + /// + public string? UpdateReason { get; set; } +} diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommandHandler.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommandHandler.cs new file mode 100644 index 0000000..99e845c --- /dev/null +++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Commands/UpdateAppVersion/UpdateAppVersionCommandHandler.cs @@ -0,0 +1,39 @@ +using CMSMicroservice.Protobuf.Protos.AppVersion; + +namespace BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion; + +public class UpdateAppVersionCommandHandler : IRequestHandler +{ + private readonly IApplicationContractContext _context; + + public UpdateAppVersionCommandHandler(IApplicationContractContext context) + { + _context = context; + } + + public async Task Handle(UpdateAppVersionCommand request, CancellationToken cancellationToken) + { + var cmsRequest = new UpdateAppVersionRequest + { + AppName = request.AppName, + CurrentVersion = request.CurrentVersion, + RequiresFullCacheClear = request.RequiresFullCacheClear + }; + + if (!string.IsNullOrEmpty(request.MinRequiredVersion)) + cmsRequest.MinRequiredVersion = request.MinRequiredVersion; + + if (!string.IsNullOrEmpty(request.UpdateMessage)) + cmsRequest.UpdateMessage = request.UpdateMessage; + + if (!string.IsNullOrEmpty(request.ReleaseNotes)) + cmsRequest.ReleaseNotes = request.ReleaseNotes; + + if (!string.IsNullOrEmpty(request.UpdateReason)) + cmsRequest.UpdateReason = request.UpdateReason; + + await _context.AppVersions.UpdateAppVersionAsync(cmsRequest, cancellationToken: cancellationToken); + + return Unit.Value; + } +} diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQuery.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQuery.cs new file mode 100644 index 0000000..12321a5 --- /dev/null +++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQuery.cs @@ -0,0 +1,6 @@ +namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions; + +public class GetAllAppVersionsQuery : IRequest +{ + public bool IncludeInactive { get; set; } +} diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQueryHandler.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQueryHandler.cs new file mode 100644 index 0000000..a9215aa --- /dev/null +++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsQueryHandler.cs @@ -0,0 +1,40 @@ +using CMSMicroservice.Protobuf.Protos.AppVersion; + +namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions; + +public class GetAllAppVersionsQueryHandler : IRequestHandler +{ + private readonly IApplicationContractContext _context; + + public GetAllAppVersionsQueryHandler(IApplicationContractContext context) + { + _context = context; + } + + public async Task Handle(GetAllAppVersionsQuery request, CancellationToken cancellationToken) + { + var cmsRequest = new GetAllAppVersionsRequest + { + IncludeInactive = request.IncludeInactive + }; + + var response = await _context.AppVersions.GetAllAppVersionsAsync(cmsRequest, cancellationToken: cancellationToken); + + return new GetAllAppVersionsResponseDto + { + Items = response.Items.Select(item => new AppVersionItemDto + { + Id = item.Id, + AppName = item.AppName, + CurrentVersion = item.CurrentVersion, + MinRequiredVersion = item.MinRequiredVersion, + RequiresFullCacheClear = item.RequiresFullCacheClear, + UpdateMessage = item.UpdateMessage, + ReleaseNotes = item.ReleaseNotes, + IsActive = item.IsActive, + Created = item.Created?.ToDateTimeOffset() ?? DateTimeOffset.MinValue, + LastModified = item.LastModified?.ToDateTimeOffset() ?? DateTimeOffset.MinValue + }).ToList() + }; + } +} diff --git a/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsResponseDto.cs b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsResponseDto.cs new file mode 100644 index 0000000..0b731a3 --- /dev/null +++ b/src/BackOffice.BFF.Application/ConfigurationCQ/Queries/GetAllAppVersions/GetAllAppVersionsResponseDto.cs @@ -0,0 +1,20 @@ +namespace BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions; + +public class GetAllAppVersionsResponseDto +{ + public List Items { get; set; } = new(); +} + +public class AppVersionItemDto +{ + public long Id { get; set; } + public string AppName { get; set; } = string.Empty; + public string CurrentVersion { get; set; } = string.Empty; + public string MinRequiredVersion { get; set; } = string.Empty; + public bool RequiresFullCacheClear { get; set; } + public string? UpdateMessage { get; set; } + public string? ReleaseNotes { get; set; } + public bool IsActive { get; set; } + public DateTimeOffset Created { get; set; } + public DateTimeOffset LastModified { get; set; } +} diff --git a/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj b/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj index 09728be..2625fba 100644 --- a/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj +++ b/src/BackOffice.BFF.Domain/BackOffice.BFF.Domain.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs b/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs index 4aaf429..0d5c085 100644 --- a/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs +++ b/src/BackOffice.BFF.Infrastructure/Services/ApplicationContractContext.cs @@ -24,6 +24,7 @@ using CMSMicroservice.Protobuf.Protos.Configuration; using CMSMicroservice.Protobuf.Protos.DiscountOrder; using CMSMicroservice.Protobuf.Protos.ManualPayment; using CMSMicroservice.Protobuf.Protos.NetworkMembership; +using CMSMicroservice.Protobuf.Protos.AppVersion; // BFF Protobuf contracts @@ -93,5 +94,8 @@ public class ApplicationContractContext : IApplicationContractContext // Manual Payments (Admin) public ManualPaymentContract.ManualPaymentContractClient ManualPayments => GetService(); + + // App Version Management + public AppVersionContract.AppVersionContractClient AppVersions => GetService(); #endregion } diff --git a/src/BackOffice.BFF.WebApi/Common/Mappings/AppVersionProfile.cs b/src/BackOffice.BFF.WebApi/Common/Mappings/AppVersionProfile.cs new file mode 100644 index 0000000..913b77f --- /dev/null +++ b/src/BackOffice.BFF.WebApi/Common/Mappings/AppVersionProfile.cs @@ -0,0 +1,42 @@ +using BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion; +using BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions; +using Google.Protobuf.WellKnownTypes; +using ProtoDto = BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion; + +namespace BackOffice.BFF.WebApi.Common.Mappings; + +public class AppVersionProfile : IRegister +{ + void IRegister.Register(TypeAdapterConfig config) + { + // Request -> Query mappings + config.NewConfig() + .Map(dest => dest.IncludeInactive, src => src.IncludeInactive); + + // Request -> Command mappings + config.NewConfig() + .Map(dest => dest.AppName, src => src.AppName) + .Map(dest => dest.CurrentVersion, src => src.CurrentVersion) + .Map(dest => dest.MinRequiredVersion, src => src.MinRequiredVersion) + .Map(dest => dest.RequiresFullCacheClear, src => src.RequiresFullCacheClear) + .Map(dest => dest.UpdateMessage, src => src.UpdateMessage) + .Map(dest => dest.ReleaseNotes, src => src.ReleaseNotes) + .Map(dest => dest.UpdateReason, src => src.UpdateReason); + + // Response mappings + config.NewConfig() + .Map(dest => dest.Items, src => src.Items); + + config.NewConfig() + .Map(dest => dest.Id, src => src.Id) + .Map(dest => dest.AppName, src => src.AppName) + .Map(dest => dest.CurrentVersion, src => src.CurrentVersion) + .Map(dest => dest.MinRequiredVersion, src => src.MinRequiredVersion) + .Map(dest => dest.RequiresFullCacheClear, src => src.RequiresFullCacheClear) + .Map(dest => dest.UpdateMessage, src => src.UpdateMessage ?? string.Empty) + .Map(dest => dest.ReleaseNotes, src => src.ReleaseNotes ?? string.Empty) + .Map(dest => dest.IsActive, src => src.IsActive) + .Map(dest => dest.Created, src => Timestamp.FromDateTimeOffset(src.Created)) + .Map(dest => dest.LastModified, src => Timestamp.FromDateTimeOffset(src.LastModified)); + } +} diff --git a/src/BackOffice.BFF.WebApi/Services/AppVersionService.cs b/src/BackOffice.BFF.WebApi/Services/AppVersionService.cs new file mode 100644 index 0000000..95d0186 --- /dev/null +++ b/src/BackOffice.BFF.WebApi/Services/AppVersionService.cs @@ -0,0 +1,55 @@ +using BackOffice.BFF.WebApi.Common.Services; +using BackOffice.BFF.Application.ConfigurationCQ.Commands.UpdateAppVersion; +using BackOffice.BFF.Application.ConfigurationCQ.Queries.GetAllAppVersions; +using BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion; + +namespace BackOffice.BFF.WebApi.Services; + +public class AppVersionService : AppVersionContract.AppVersionContractBase +{ + private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS; + + public AppVersionService(IDispatchRequestToCQRS dispatchRequestToCQRS) + { + _dispatchRequestToCQRS = dispatchRequestToCQRS; + } + + [RequiresPermission(PermissionNames.SettingsView)] + public override async Task GetAllAppVersions( + GetAllAppVersionsRequest request, + ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle( + request, + context); + } + + [RequiresPermission(PermissionNames.SettingsView)] + public override async Task GetAppVersion( + GetAppVersionRequest request, + ServerCallContext context) + { + // For single app version, we use GetAll and filter + var allVersionsResponse = await _dispatchRequestToCQRS.Handle( + new GetAllAppVersionsRequest { IncludeInactive = false }, + context); + + var item = allVersionsResponse.Items.FirstOrDefault(x => x.AppName == request.AppName); + + return new GetAppVersionResponse + { + Found = item != null, + Item = item + }; + } + + [RequiresPermission(PermissionNames.SettingsManageConfiguration)] + public override async Task UpdateAppVersion( + UpdateAppVersionRequest request, + ServerCallContext context) + { + return await _dispatchRequestToCQRS.Handle( + request, + context); + } +} diff --git a/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/BackOffice.BFF.Configuration.Protobuf.csproj b/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/BackOffice.BFF.Configuration.Protobuf.csproj index 1c3baae..d103ba5 100644 --- a/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/BackOffice.BFF.Configuration.Protobuf.csproj +++ b/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/BackOffice.BFF.Configuration.Protobuf.csproj @@ -6,7 +6,7 @@ enable true Foursat.BackOffice.BFF.Configuration.Protobuf - 1.0.7 + 1.0.20 FourSat FourSat Foursat.BackOffice.BFF.Configuration.Protobuf @@ -25,6 +25,7 @@ + diff --git a/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto b/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto new file mode 100644 index 0000000..76f9471 --- /dev/null +++ b/src/Protobufs/BackOffice.BFF.Configuration.Protobuf/Protos/appversion.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package appversion; + +import "google/protobuf/empty.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; +import "google/api/annotations.proto"; + +option csharp_namespace = "BackOffice.BFF.Configuration.Protobuf.Protos.AppVersion"; + +// Service for managing app versions (admin panel) +service AppVersionContract +{ + // Get all app versions + rpc GetAllAppVersions(GetAllAppVersionsRequest) returns (GetAllAppVersionsResponse){ + option (google.api.http) = { + get: "/AppVersion/GetAll" + }; + }; + + // Get single app version + rpc GetAppVersion(GetAppVersionRequest) returns (GetAppVersionResponse){ + option (google.api.http) = { + get: "/AppVersion/Get" + }; + }; + + // Update/Create app version + rpc UpdateAppVersion(UpdateAppVersionRequest) returns (google.protobuf.Empty){ + option (google.api.http) = { + post: "/AppVersion/Update" + body: "*" + }; + }; +} + +// Request to get all app versions +message GetAllAppVersionsRequest +{ + bool include_inactive = 1; +} + +// Response with all app versions +message GetAllAppVersionsResponse +{ + repeated AppVersionItem items = 1; +} + +// Request to get single app version +message GetAppVersionRequest +{ + string app_name = 1; +} + +// Response with version info +message GetAppVersionResponse +{ + bool found = 1; + AppVersionItem item = 2; +} + +// Request to update/create app version +message UpdateAppVersionRequest +{ + string app_name = 1; + string current_version = 2; + google.protobuf.StringValue min_required_version = 3; + bool requires_full_cache_clear = 4; + google.protobuf.StringValue update_message = 5; + google.protobuf.StringValue release_notes = 6; + google.protobuf.StringValue update_reason = 7; // For admin logs +} + +// App version item +message AppVersionItem +{ + int64 id = 1; + string app_name = 2; + string current_version = 3; + string min_required_version = 4; + bool requires_full_cache_clear = 5; + string update_message = 6; + string release_notes = 7; + bool is_active = 8; + google.protobuf.Timestamp created = 9; + google.protobuf.Timestamp last_modified = 10; +}