From 45f95b3184442aa35230cf7111dddd607f74ca7b Mon Sep 17 00:00:00 2001 From: dibakor Date: Mon, 10 Aug 2026 17:35:41 +0600 Subject: [PATCH] change material price endpoint request --- .../Global/AppSettings.cs | 3 +- .../IntegrationMaterialFreeGoodsRequest.cs | 2 +- .../IntegrationMaterialPriceRequest.cs | 22 ++++- .../IntegrationMaterialRequest.cs | 2 +- .../Integrations/IntegrationService.cs | 74 +++++++++----- ...-115_f855c45771f44710a3d54f20a3ed6b60.json | 16 +++ ...-241_f3421f3fecff431baf173b22213d8ab0.json | 16 +++ ...-383_673926f73cdf4adfa98ae6f9852995b6.json | 24 +++++ ...-898_1c97d426dd29416fa570085bdcec3840.json | 27 +++++ .../Converter/NullableDecimalConverter.cs | 50 ++++++++++ Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs | 99 ++++++++++++++++++- .../appsettings.json | 1 + 12 files changed, 301 insertions(+), 35 deletions(-) create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-01-115_f855c45771f44710a3d54f20a3ed6b60.json create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-22-241_f3421f3fecff431baf173b22213d8ab0.json create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-22-54-383_673926f73cdf4adfa98ae6f9852995b6.json create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-23-57-898_1c97d426dd29416fa570085bdcec3840.json create mode 100644 Api/OnlineSalesAutoCrop.CoreAPI/Converter/NullableDecimalConverter.cs diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs index e945499..5ac8735 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Global/AppSettings.cs @@ -55,7 +55,8 @@ namespace OnlineSalesAutoCrop.CoreAPI.Models.Global public bool JwtValidateAudience { get; set; } public int RefreshTokenDuration { get; set; } public int AccessTokenDuration { get; set; } - + public int IsLogRequest { get; set; } = 0; + /// /// Folder management /// diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialFreeGoodsRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialFreeGoodsRequest.cs index ea41442..e9c4096 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialFreeGoodsRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialFreeGoodsRequest.cs @@ -26,7 +26,7 @@ public class IntegrationMaterialFreeGoodsRequest [StringLength(2, MinimumLength = 1, ErrorMessage = "CustomerPriceGroup must be between 1 and 2 characters.")] public string? CustomerPriceGroup { get; set; } - [StringLength(10, MinimumLength = 1, ErrorMessage = "MaterialCode must be between 1 and 10 characters.")] + [StringLength(18, MinimumLength = 1, ErrorMessage = "MaterialCode must be between 1 and 18 characters.")] public string? MaterialCode { get; set; } public IList Slabs { get; set; } } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialPriceRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialPriceRequest.cs index 5faefed..199743c 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialPriceRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialPriceRequest.cs @@ -27,7 +27,7 @@ public class IntegrationMaterialPriceRequest [StringLength(10, ErrorMessage = "Customer Group Code cannot exceed 10 characters.")] public string? CustomerGroupCode { get; set; } - [StringLength(10, ErrorMessage = "Material Code cannot exceed 10 characters.")] + [StringLength(18, ErrorMessage = "Material Code cannot exceed 18 characters.")] public string? MaterialCode { get; set; } [Required(ErrorMessage = "Condition Type is required.")] @@ -43,12 +43,28 @@ public class IntegrationMaterialPriceRequest [StringLength(3, ErrorMessage = "Unit Of Measure cannot exceed 3 characters.")] public string? UnitOfMeasure { get; set; } + + public decimal? ScaleValue { get; set; } + + [Required(ErrorMessage = "Valid From is Required")] + public DateTime ValidFrom { get; set; } + + [Required(ErrorMessage = "Valid To is Required")] + public DateTime ValidTo { get; set; } + + [StringLength(1, ErrorMessage = "Status cannot exceed 1 character.")] + [RegularExpression(@"^$|^X$", ErrorMessage = "Status must be blank (Active) or 'X' (Inactive).")] + public string Status { get; set; } + + [Required(ErrorMessage = "Condition Value is required.")] + [Range(typeof(decimal), "0", "999999999999999999.99", ErrorMessage = "Condition Value must be greater than or equal to 0.")] + public decimal ConditionValue { get; set; } + public IList Slabs { get; set; } } public class IntegrationMaterialPriceItemRequest { - [Range(typeof(decimal), "0", "999999999999999999.99", ErrorMessage = "Scale Value must be greater than or equal to 0.")] public decimal? ScaleValue { get; set; } [Required(ErrorMessage ="Valid From is Required")] @@ -73,7 +89,7 @@ public class GetMaterialPriceByCodeRequest public string SalesOrg { get; set; } [Required(ErrorMessage = "Material Code is required.")] - [StringLength(10, ErrorMessage = "Material Code cannot exceed 10 characters.")] + [StringLength(18, ErrorMessage = "Material Code cannot exceed 18 characters.")] public string MaterialCode { get; set; } [Required(ErrorMessage = "Condition Type is required.")] diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialRequest.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialRequest.cs index 90aa41e..9e14d58 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialRequest.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Models/Requests/Integrations/IntegrationMaterialRequest.cs @@ -13,7 +13,7 @@ public class IntegrationMaterialRequest public string MaterialTypeDescription { get; set; } [Required(ErrorMessage = "Material code is required.")] - [StringLength(10, ErrorMessage = "Material code cannot exceed 10 characters.")] + [StringLength(18, ErrorMessage = "Material code cannot exceed 18 characters.")] public string MaterialCode { get; set; } [Required(ErrorMessage = "Material description is required.")] diff --git a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationService.cs b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationService.cs index 0c98d58..da6abe9 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationService.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI.Services/Services/Integrations/IntegrationService.cs @@ -135,7 +135,7 @@ public class IntegrationService : IIntegrationService try { IntegrationEmployeeResponse employee = await GetEmployeeBySalesOrgAsync(tc, - new EmployeeIntegrationByComapanyRequest() { EmployeeVendorCode = request.EmployeeVendorCode, CompanyCode = request.CompanyCode }); + new EmployeeIntegrationByComapanyRequest() { EmployeeVendorCode = request.EmployeeVendorCode, CompanyCode = request.SalesOrg }); if (employee != null && employee.EmployeeId > 0) @@ -1306,29 +1306,57 @@ public class IntegrationService : IIntegrationService try { - foreach (var item in request.Slabs) + if(request.Slabs!= null && request.Slabs.Count > 0) { - SqlParameter[] p = - [ - SqlHelperExtension.CreateInParam("@SalesOrg", SqlDbType.NVarChar, request.SalesOrg), - SqlHelperExtension.CreateInParam("@DistributionChannel", SqlDbType.NVarChar, request.DistributionChannel), - SqlHelperExtension.CreateInParam("@CustomerNumber", SqlDbType.NVarChar, request.CustomerNumber), - SqlHelperExtension.CreateInParam("@CustomerGroupCode", SqlDbType.NVarChar, request.CustomerGroupCode), - SqlHelperExtension.CreateInParam("@MaterialCode", SqlDbType.NVarChar, request.MaterialCode), - SqlHelperExtension.CreateInParam("@ConditionType", SqlDbType.NVarChar, request.ConditionType), - SqlHelperExtension.CreateInParam("@CalculationType", SqlDbType.NVarChar, request.CalculationType), - SqlHelperExtension.CreateInParam("@PricingUnit", SqlDbType.NVarChar, request.PricingUnit), - SqlHelperExtension.CreateInParam("@UnitOfMeasure", SqlDbType.NVarChar, request.UnitOfMeasure), - SqlHelperExtension.CreateInParam("@ScaleValue", SqlDbType.NVarChar, item.ScaleValue), - SqlHelperExtension.CreateInParam("@ValidFrom", SqlDbType.DateTime, item.ValidFrom), - SqlHelperExtension.CreateInParam("@ValidTo", SqlDbType.DateTime, item.ValidTo), - SqlHelperExtension.CreateInParam("@Status", SqlDbType.NVarChar, item.Status), - SqlHelperExtension.CreateInParam("@ConditionValue", SqlDbType.Decimal, item.ConditionValue) , - SqlHelperExtension.CreateInParam("@ConditionRecNo", SqlDbType.Int, request.ConditionRecNo) , - ]; + foreach (var item in request.Slabs) + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam("@SalesOrg", SqlDbType.NVarChar, request.SalesOrg), + SqlHelperExtension.CreateInParam("@DistributionChannel", SqlDbType.NVarChar, request.DistributionChannel), + SqlHelperExtension.CreateInParam("@CustomerNumber", SqlDbType.NVarChar, request.CustomerNumber), + SqlHelperExtension.CreateInParam("@CustomerGroupCode", SqlDbType.NVarChar, request.CustomerGroupCode), + SqlHelperExtension.CreateInParam("@MaterialCode", SqlDbType.NVarChar, request.MaterialCode), + SqlHelperExtension.CreateInParam("@ConditionType", SqlDbType.NVarChar, request.ConditionType), + SqlHelperExtension.CreateInParam("@CalculationType", SqlDbType.NVarChar, request.CalculationType), + SqlHelperExtension.CreateInParam("@PricingUnit", SqlDbType.NVarChar, request.PricingUnit), + SqlHelperExtension.CreateInParam("@UnitOfMeasure", SqlDbType.NVarChar, request.UnitOfMeasure), + SqlHelperExtension.CreateInParam("@ScaleValue", SqlDbType.NVarChar, item.ScaleValue), + SqlHelperExtension.CreateInParam("@ValidFrom", SqlDbType.DateTime, item.ValidFrom), + SqlHelperExtension.CreateInParam("@ValidTo", SqlDbType.DateTime, item.ValidTo), + SqlHelperExtension.CreateInParam("@Status", SqlDbType.NVarChar, item.Status), + SqlHelperExtension.CreateInParam("@ConditionValue", SqlDbType.Decimal, item.ConditionValue) , + SqlHelperExtension.CreateInParam("@ConditionRecNo", SqlDbType.Int, request.ConditionRecNo) , + ]; + + _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertIntegrationMaterialPrice", parameterValues: p); + } + } + else + { + SqlParameter[] p = + [ + SqlHelperExtension.CreateInParam("@SalesOrg", SqlDbType.NVarChar, request.SalesOrg), + SqlHelperExtension.CreateInParam("@DistributionChannel", SqlDbType.NVarChar, request.DistributionChannel), + SqlHelperExtension.CreateInParam("@CustomerNumber", SqlDbType.NVarChar, request.CustomerNumber), + SqlHelperExtension.CreateInParam("@CustomerGroupCode", SqlDbType.NVarChar, request.CustomerGroupCode), + SqlHelperExtension.CreateInParam("@MaterialCode", SqlDbType.NVarChar, request.MaterialCode), + SqlHelperExtension.CreateInParam("@ConditionType", SqlDbType.NVarChar, request.ConditionType), + SqlHelperExtension.CreateInParam("@CalculationType", SqlDbType.NVarChar, request.CalculationType), + SqlHelperExtension.CreateInParam("@PricingUnit", SqlDbType.NVarChar, request.PricingUnit), + SqlHelperExtension.CreateInParam("@UnitOfMeasure", SqlDbType.NVarChar, request.UnitOfMeasure), + SqlHelperExtension.CreateInParam("@ScaleValue", SqlDbType.NVarChar, Convert.ToDecimal(request.PricingUnit ?? "0")), + SqlHelperExtension.CreateInParam("@ValidFrom", SqlDbType.DateTime, request.ValidFrom), + SqlHelperExtension.CreateInParam("@ValidTo", SqlDbType.DateTime, request.ValidTo), + SqlHelperExtension.CreateInParam("@Status", SqlDbType.NVarChar, request.Status), + SqlHelperExtension.CreateInParam("@ConditionValue", SqlDbType.Decimal, request.ConditionValue) , + SqlHelperExtension.CreateInParam("@ConditionRecNo", SqlDbType.Int, request.ConditionRecNo) , + ]; _ = tc.ExecuteNonQuerySp(spName: "dbo.InsertIntegrationMaterialPrice", parameterValues: p); } + + response = true; } catch (Exception) @@ -1948,12 +1976,6 @@ public class IntegrationService : IIntegrationService break; } - if (request.Slabs == null || !request.Slabs.Any()) - { - errors.Add("At least one slab is required."); - return errors; - } - return errors; } diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-01-115_f855c45771f44710a3d54f20a3ed6b60.json b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-01-115_f855c45771f44710a3d54f20a3ed6b60.json new file mode 100644 index 0000000..fa26f2b --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-01-115_f855c45771f44710a3d54f20a3ed6b60.json @@ -0,0 +1,16 @@ +{ + "timestamp": "2026-08-10T06:07:01.1034033+00:00", + "request": { + "method": "GET", + "scheme": "http", + "host": "localhost:4204", + "path": "/", + "query": "", + "ipAddress": "127.0.0.1", + "body": "" + }, + "response": { + "statusCode": 200, + "body": "\u003Cb\u003EEase Taskforce Api is Running\u003C/b\u003E" + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-22-241_f3421f3fecff431baf173b22213d8ab0.json b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-22-241_f3421f3fecff431baf173b22213d8ab0.json new file mode 100644 index 0000000..76b6059 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-07-22-241_f3421f3fecff431baf173b22213d8ab0.json @@ -0,0 +1,16 @@ +{ + "timestamp": "2026-08-10T06:07:22.2388472+00:00", + "request": { + "method": "POST", + "scheme": "http", + "host": "localhost:4204", + "path": "/api/v1/IntegrationAuth/login", + "query": "", + "ipAddress": "::1", + "body": "{\r\n \u0022loginId\u0022 : \u0022SAPUser\u0022,\r\n \u0022password\u0022 : \u0022SapUser@#1213@%!\u0022\r\n}" + }, + "response": { + "statusCode": 200, + "body": "{\u0022loginId\u0022:\u0022SAPUser\u0022,\u0022accessToken\u0022:\u0022eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJMb2dpbklkIjoiU0FQVXNlciIsIkVtYWlsIjoiIiwiQXV0aEtleSI6IiIsIkhhc2hLZXkiOiJmOWE5ODgxNC1mYjgxLTRkYjctYjNkZi04OGM5MzVjNjgyYTYiLCJuYmYiOjE3ODYzNDIwNDIsImV4cCI6MTc4NjM4NTI0MiwiaWF0IjoxNzg2MzQyMDQyfQ.VhHYVb58-iXAe6uJfK-JddsssTO3pGzoMj0zeSrOjv290uWj6X7VbquntXHmKUBcrMabRBtjwPT7YdQZn0njwQ\u0022,\u0022refreshToken\u0022:\u0022VsChwEpKHMb7eFC2ZVK/o8uRbZ/0ochGgsAFR1lKpH4=\u0022,\u0022loginStatus\u0022:0,\u0022accessTokenExpiry\u0022:\u00222026-08-10T12:09:22.1883677\u002B06:00\u0022,\u0022returnStatus\u0022:200,\u0022returnMessage\u0022:[]}" + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-22-54-383_673926f73cdf4adfa98ae6f9852995b6.json b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-22-54-383_673926f73cdf4adfa98ae6f9852995b6.json new file mode 100644 index 0000000..7e91c64 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-22-54-383_673926f73cdf4adfa98ae6f9852995b6.json @@ -0,0 +1,24 @@ +{ + "timestamp": "2026-08-10T06:22:54.3731022+00:00", + "request": { + "method": "POST", + "scheme": "http", + "host": "localhost:4204", + "path": "/api/v1/IntegrationAuth/login", + "query": "", + "ipAddress": "::1", + "body": "{\r\n \u0022loginId\u0022 : \u0022SAPUser\u0022,\r\n \u0022password\u0022 : \u0022SapUser@#1213@%!\u0022\r\n}" + }, + "response": { + "statusCode": 200, + "body": { + "loginId": "SAPUser", + "accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJMb2dpbklkIjoiU0FQVXNlciIsIkVtYWlsIjoiIiwiQXV0aEtleSI6IiIsIkhhc2hLZXkiOiJmYjE5OWZlMi0zMGVlLTQ2YjItOWYzNC1lNjI4ZTY5NTkzN2QiLCJuYmYiOjE3ODYzNDI5NzQsImV4cCI6MTc4NjM4NjE3MywiaWF0IjoxNzg2MzQyOTc0fQ.NQH786tBsAdB-oToocQzbV4t_esZwR3t2gszdZCFnD5xanE4So-PnXmOtZKjfIIgP6AEcIek5_FqMz8fVwWkXg", + "refreshToken": "jolD3tih1hLRiGNi/4WvLJDCTyNUJKuRJHEWoTDk7X4=", + "loginStatus": 0, + "accessTokenExpiry": "2026-08-10T12:24:54.3150635\u002B06:00", + "returnStatus": 200, + "returnMessage": [] + } + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-23-57-898_1c97d426dd29416fa570085bdcec3840.json b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-23-57-898_1c97d426dd29416fa570085bdcec3840.json new file mode 100644 index 0000000..9ad6650 --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/ApiLogs/2026-08-10_12-23-57-898_1c97d426dd29416fa570085bdcec3840.json @@ -0,0 +1,27 @@ +{ + "timestamp": "2026-08-10T06:23:57.8920082+00:00", + "request": { + "method": "POST", + "scheme": "http", + "host": "localhost:4204", + "path": "/api/v1/IntegrationAuth/login", + "query": "", + "ipAddress": "::1", + "body": { + "loginId": "SAPUser", + "password": "SapUser@#1213@%!" + } + }, + "response": { + "statusCode": 200, + "body": { + "loginId": "SAPUser", + "accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJMb2dpbklkIjoiU0FQVXNlciIsIkVtYWlsIjoiIiwiQXV0aEtleSI6IiIsIkhhc2hLZXkiOiJhYTQ0NjAwZC0wM2VmLTQxN2QtOTM0Mi1lNmVhNDZhYWFkMzEiLCJuYmYiOjE3ODYzNDMwMzcsImV4cCI6MTc4NjM4NjIzNywiaWF0IjoxNzg2MzQzMDM3fQ.ML8boRFCmpgRiXOOjBLPihcJ4lTfqQnRJru9z66uc2Ovp2U3sbs4VHGTaVA0dOXW809humaicRBlspWptOKCfg", + "refreshToken": "nZfBF\u002BkjKgl9ObZXLHvaNb69d07e3jOubNRXdA3D8BQ=", + "loginStatus": 0, + "accessTokenExpiry": "2026-08-10T12:25:57.7816164\u002B06:00", + "returnStatus": 200, + "returnMessage": [] + } + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Converter/NullableDecimalConverter.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Converter/NullableDecimalConverter.cs new file mode 100644 index 0000000..b7bc7ee --- /dev/null +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Converter/NullableDecimalConverter.cs @@ -0,0 +1,50 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + + +namespace OnlineSalesAutoCrop.CoreAPI.Converter; + +public class NullableDecimalConverter : JsonConverter +{ + public override decimal? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + return null; + + if (reader.TokenType == JsonTokenType.Number) + { + var value = reader.GetInt32(); + return value == 0 ? null : value; + } + + if (reader.TokenType == JsonTokenType.String) + { + var value = reader.GetString(); + + if (string.IsNullOrWhiteSpace(value) || value == "0") + return null; + + if (int.TryParse(value, out var result)) + return result; + + throw new JsonException($"Invalid ScaleValue: {value}"); + } + + throw new JsonException("ScaleValue must be a number, string, or null."); + } + + public override void Write( + Utf8JsonWriter writer, + decimal? value, + JsonSerializerOptions options) + { + if (value is null) + writer.WriteNullValue(); + else + writer.WriteNumberValue(value.Value); + } +} \ No newline at end of file diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs index 4818f30..0ceed54 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs +++ b/Api/OnlineSalesAutoCrop.CoreAPI/Startup.cs @@ -36,6 +36,7 @@ using System.Net; using System.Net.Http.Headers; using System.Reflection; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.RateLimiting; using System.Threading.Tasks; @@ -91,6 +92,9 @@ namespace OnlineSalesAutoCrop.CoreAPI { options.JsonSerializerOptions.Converters.Add( new CoreAPI.Converter.EmptyStringToNullConverter()); + options.JsonSerializerOptions.Converters.Add( + new CoreAPI.Converter.NullableDecimalConverter()); + }); services.Configure(options => @@ -515,11 +519,100 @@ namespace OnlineSalesAutoCrop.CoreAPI loggerFactory.AddFile(pathFormat: $@"{_appSettings.LoggerPath}\Log.txt", minimumLevel: (LogLevel)_appSettings.LoggerMinLevel); } - #endregion + #endregion - #region Controllers and SignalR + #region Log Request and Response - app.UseEndpoints(endpoints => + if (_appSettings.IsLogRequest > 0) + { + app.Use(async (context, next) => + { + var logDirectory = Path.Combine( + env.ContentRootPath, + "ApiLogs" + ); + + context.Request.EnableBuffering(); + + string requestBody = ""; + + if (context.Request.ContentLength > 0) + { + using var reader = new StreamReader( + context.Request.Body, + Encoding.UTF8, + leaveOpen: true); + + requestBody = await reader.ReadToEndAsync(); + + context.Request.Body.Position = 0; + } + + var originalResponseBody = context.Response.Body; + + await using var responseBody = new MemoryStream(); + context.Response.Body = responseBody; + + try + { + await next(); + + responseBody.Position = 0; + + var responseText = + await new StreamReader(responseBody).ReadToEndAsync(); + + responseBody.Position = 0; + + await responseBody.CopyToAsync(originalResponseBody); + + var log = new + { + timestamp = DateTimeOffset.UtcNow, + + request = new + { + method = context.Request.Method, + scheme = context.Request.Scheme, + host = context.Request.Host.ToString(), + path = context.Request.Path.ToString(), + query = context.Request.QueryString.ToString(), + ipAddress = context.Connection.RemoteIpAddress?.ToString(), + body = JsonDocument.Parse(requestBody).RootElement + }, + + response = new + { + statusCode = context.Response.StatusCode, + body = JsonDocument.Parse(responseText).RootElement + } + }; + + var json = System.Text.Json.JsonSerializer.Serialize( + log, + new JsonSerializerOptions + { + WriteIndented = true + }); + + var fileName = + $"{DateTime.Now:yyyy-MM-dd_HH-mm-ss-fff}.json"; + + var filePath = Path.Combine(logDirectory, fileName); + + await File.WriteAllTextAsync(filePath, json); + } + finally + { + context.Response.Body = originalResponseBody; + } + }); + } + + #endregion + #region Controllers and SignalR + + app.UseEndpoints(endpoints => { if (!string.IsNullOrEmpty(_appSettings.RLName)) { diff --git a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json index 5a400f3..6114ce6 100644 --- a/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json +++ b/Api/OnlineSalesAutoCrop.CoreAPI/appsettings.json @@ -86,6 +86,7 @@ "WaSenderId": "+8801326755660", "RefreshTokenDuration": "2", "AccessTokenDuration": "1", + "IsLogRequest": 0, "AutoCropSAPApi": { "BaseUrl": "https://accl-test-pad-l06vsvh7.it-cpi004-rt.cfapps.ap11.hana.ondemand.com/", "UserName": "sb-1635ae0e-9941-4998-962f-e23f592d29b5!b45657|it-rt-accl-test-pad-l06vsvh7!b68",