From 2f4d968842c346c6c39beee3d877a0ff86b7d844 Mon Sep 17 00:00:00 2001 From: KrzysztofPajak Date: Wed, 1 Apr 2026 20:35:01 +0200 Subject: [PATCH 1/3] refactor: migrate JsonPatch from Newtonsoft to System.Text.Json (.NET 10) --- Directory.Packages.props | 3 +-- .../Controllers/BrandController.cs | 2 +- .../Controllers/CategoryController.cs | 2 +- .../Controllers/CollectionController.cs | 2 +- .../Controllers/CustomerGroupController.cs | 2 +- .../Controllers/ProductAttributeController.cs | 2 +- .../Controllers/ProductController.cs | 2 +- .../SpecificationAttributeController.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 18 ------------------ .../Grand.Module.Api/Grand.Module.Api.csproj | 3 +-- .../Infrastructure/OpenApiStartup.cs | 5 ----- .../IgnoreFieldSchemaTransformer.cs | 6 +++--- src/Web/Grand.Web/App_Data/appsettings.json | 6 +++++- 13 files changed, 17 insertions(+), 38 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ab25cfd27..b2786b10f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -36,8 +36,7 @@ - - + diff --git a/src/Modules/Grand.Module.Api/Controllers/BrandController.cs b/src/Modules/Grand.Module.Api/Controllers/BrandController.cs index 7d22fec2f..2bfc81ef2 100644 --- a/src/Modules/Grand.Module.Api/Controllers/BrandController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/BrandController.cs @@ -5,7 +5,7 @@ using Grand.Domain.Permissions; using Grand.Domain.Catalog; using MediatR; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Http; using Grand.Module.Api.Attributes; diff --git a/src/Modules/Grand.Module.Api/Controllers/CategoryController.cs b/src/Modules/Grand.Module.Api/Controllers/CategoryController.cs index 0d1303617..c41b54cd3 100644 --- a/src/Modules/Grand.Module.Api/Controllers/CategoryController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/CategoryController.cs @@ -5,7 +5,7 @@ using Grand.Domain.Permissions; using Grand.Domain.Catalog; using MediatR; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Grand.Module.Api.Attributes; using Microsoft.AspNetCore.Http; diff --git a/src/Modules/Grand.Module.Api/Controllers/CollectionController.cs b/src/Modules/Grand.Module.Api/Controllers/CollectionController.cs index 036645b39..e396d3cbf 100644 --- a/src/Modules/Grand.Module.Api/Controllers/CollectionController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/CollectionController.cs @@ -7,7 +7,7 @@ using Grand.Module.Api.Queries.Models.Common; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; diff --git a/src/Modules/Grand.Module.Api/Controllers/CustomerGroupController.cs b/src/Modules/Grand.Module.Api/Controllers/CustomerGroupController.cs index 7f7080181..286b754b6 100644 --- a/src/Modules/Grand.Module.Api/Controllers/CustomerGroupController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/CustomerGroupController.cs @@ -7,7 +7,7 @@ using Grand.Module.Api.Queries.Models.Common; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; diff --git a/src/Modules/Grand.Module.Api/Controllers/ProductAttributeController.cs b/src/Modules/Grand.Module.Api/Controllers/ProductAttributeController.cs index e932377f0..1122c1ceb 100644 --- a/src/Modules/Grand.Module.Api/Controllers/ProductAttributeController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/ProductAttributeController.cs @@ -7,7 +7,7 @@ using Grand.Module.Api.Queries.Models.Common; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; diff --git a/src/Modules/Grand.Module.Api/Controllers/ProductController.cs b/src/Modules/Grand.Module.Api/Controllers/ProductController.cs index d70465307..9ec2af815 100644 --- a/src/Modules/Grand.Module.Api/Controllers/ProductController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/ProductController.cs @@ -7,7 +7,7 @@ using Grand.Module.Api.Queries.Models.Common; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; diff --git a/src/Modules/Grand.Module.Api/Controllers/SpecificationAttributeController.cs b/src/Modules/Grand.Module.Api/Controllers/SpecificationAttributeController.cs index 7836c3a1e..4c7dca241 100644 --- a/src/Modules/Grand.Module.Api/Controllers/SpecificationAttributeController.cs +++ b/src/Modules/Grand.Module.Api/Controllers/SpecificationAttributeController.cs @@ -7,7 +7,7 @@ using Grand.Module.Api.Queries.Models.Common; using MediatR; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.JsonPatch; +using Microsoft.AspNetCore.JsonPatch.SystemTextJson; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; diff --git a/src/Modules/Grand.Module.Api/Extensions/ServiceCollectionExtensions.cs b/src/Modules/Grand.Module.Api/Extensions/ServiceCollectionExtensions.cs index c9195e699..6afdfbcee 100644 --- a/src/Modules/Grand.Module.Api/Extensions/ServiceCollectionExtensions.cs +++ b/src/Modules/Grand.Module.Api/Extensions/ServiceCollectionExtensions.cs @@ -13,30 +13,12 @@ using Grand.Module.Api.Queries.Handlers.Common; using Grand.Module.Api.Queries.Models.Common; using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; namespace Grand.Module.Api.Infrastructure.Extensions { public static class ServiceCollectionExtensions { - public static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter(this IServiceCollection services) - { - var builder = new ServiceCollection() - .AddLogging() - .AddMvc() - .AddNewtonsoftJson() - .Services.BuildServiceProvider(); - - return builder - .GetRequiredService>() - .Value - .InputFormatters - .OfType() - .First(); - } public static void RegisterRequestHandler(this IServiceCollection services) { var handlerTypes = new (Type dto, Type entity)[] diff --git a/src/Modules/Grand.Module.Api/Grand.Module.Api.csproj b/src/Modules/Grand.Module.Api/Grand.Module.Api.csproj index c1fc46b3d..765ced4aa 100644 --- a/src/Modules/Grand.Module.Api/Grand.Module.Api.csproj +++ b/src/Modules/Grand.Module.Api/Grand.Module.Api.csproj @@ -41,8 +41,7 @@ - - + diff --git a/src/Modules/Grand.Module.Api/Infrastructure/OpenApiStartup.cs b/src/Modules/Grand.Module.Api/Infrastructure/OpenApiStartup.cs index e7606dfed..746cc2c6d 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/OpenApiStartup.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/OpenApiStartup.cs @@ -54,11 +54,6 @@ public void ConfigureServices(IServiceCollection services, IConfiguration config //register RequestHandler services.RegisterRequestHandler(); - //Add JsonPatchInputFormatter - services.AddControllers(options => - { - options.InputFormatters.Insert(0, services.GetJsonPatchInputFormatter()); - }); services.AddScoped(); } } diff --git a/src/Modules/Grand.Module.Api/Infrastructure/Transformers/IgnoreFieldSchemaTransformer.cs b/src/Modules/Grand.Module.Api/Infrastructure/Transformers/IgnoreFieldSchemaTransformer.cs index 91e0b80b3..c81f90f8d 100644 --- a/src/Modules/Grand.Module.Api/Infrastructure/Transformers/IgnoreFieldSchemaTransformer.cs +++ b/src/Modules/Grand.Module.Api/Infrastructure/Transformers/IgnoreFieldSchemaTransformer.cs @@ -11,8 +11,8 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext { var type = context.JsonTypeInfo.Type; - if (!schema.Properties.Any() || type == null) return Task.CompletedTask; - + if (type == null || schema.Properties == null || !schema.Properties.Any()) return Task.CompletedTask; + var excludedPropertyNames = type .GetProperties() .Where( @@ -26,7 +26,7 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext ap => excludedPropertyNames.Any( pn => pn.Equals(ap.Key, StringComparison.InvariantCultureIgnoreCase) ) - ).Select(ap => ap.Key); + ).Select(ap => ap.Key).ToList(); foreach (var propertyToExclude in excludedSchemaPropertyKey) schema.Properties.Remove(propertyToExclude); diff --git a/src/Web/Grand.Web/App_Data/appsettings.json b/src/Web/Grand.Web/App_Data/appsettings.json index cb55d2d4a..2349f944c 100644 --- a/src/Web/Grand.Web/App_Data/appsettings.json +++ b/src/Web/Grand.Web/App_Data/appsettings.json @@ -110,7 +110,7 @@ "Grand.Module.Installer": true, "Grand.Module.Migration": true, "Grand.Module.ScheduledTasks": true, - "Grand.Module.Api": false + "Grand.Module.Api": true }, "Redis": { ///Enable the Publish/Subscribe messaging with redis to manage memory cache on every server @@ -171,6 +171,8 @@ "ClientSecret": "" }, //access to the api to web controllers + //when enabled, API documentation (Scalar) is available at: https://your-domain/scalar/ + //locally: https://localhost:44350/scalar/v2 "FrontendAPI": { "Enabled": true, "JsonContentType": false, @@ -186,6 +188,8 @@ "RefreshTokenExpiryInMinutes": 1440 }, //api for admin + //when enabled, API documentation (Scalar) is available at: https://your-domain/scalar/ + //locally: https://localhost:44350/scalar/v1 "BackendAPI": { "Enabled": true, "SecretKey": "your private secret key to use api", From a168adf20cfe83b6c89c6c69ec57ea32701c8be5 Mon Sep 17 00:00:00 2001 From: Krzysztof Pajak Date: Wed, 1 Apr 2026 20:40:07 +0200 Subject: [PATCH 2/3] Update src/Web/Grand.Web/App_Data/appsettings.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Web/Grand.Web/App_Data/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Web/Grand.Web/App_Data/appsettings.json b/src/Web/Grand.Web/App_Data/appsettings.json index 2349f944c..efa5e9aa4 100644 --- a/src/Web/Grand.Web/App_Data/appsettings.json +++ b/src/Web/Grand.Web/App_Data/appsettings.json @@ -110,7 +110,7 @@ "Grand.Module.Installer": true, "Grand.Module.Migration": true, "Grand.Module.ScheduledTasks": true, - "Grand.Module.Api": true + "Grand.Module.Api": false }, "Redis": { ///Enable the Publish/Subscribe messaging with redis to manage memory cache on every server From 387a343cc7b01217663c24668ff5ccb819df2be6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:42:24 +0000 Subject: [PATCH 3/3] Fix appsettings.json Scalar URL comments to reflect actual routes and Development-only constraint Agent-Logs-Url: https://github.com/grandnode/grandnode2/sessions/cf4ebe9c-d4e9-42da-b121-a5406393ef5d Co-authored-by: KrzysztofPajak <16772986+KrzysztofPajak@users.noreply.github.com> --- src/Web/Grand.Web/App_Data/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Web/Grand.Web/App_Data/appsettings.json b/src/Web/Grand.Web/App_Data/appsettings.json index efa5e9aa4..7ca546b1b 100644 --- a/src/Web/Grand.Web/App_Data/appsettings.json +++ b/src/Web/Grand.Web/App_Data/appsettings.json @@ -171,7 +171,7 @@ "ClientSecret": "" }, //access to the api to web controllers - //when enabled, API documentation (Scalar) is available at: https://your-domain/scalar/ + //when enabled (Development environment only), API documentation (Scalar) is available at: https://your-domain/scalar/v2 //locally: https://localhost:44350/scalar/v2 "FrontendAPI": { "Enabled": true, @@ -188,7 +188,7 @@ "RefreshTokenExpiryInMinutes": 1440 }, //api for admin - //when enabled, API documentation (Scalar) is available at: https://your-domain/scalar/ + //when enabled (Development environment only), API documentation (Scalar) is available at: https://your-domain/scalar/v1 //locally: https://localhost:44350/scalar/v1 "BackendAPI": { "Enabled": true,