Skip to content

Commit

Permalink
S05 batch operation (#25)
Browse files Browse the repository at this point in the history
* Add Batch Operation logic
  • Loading branch information
bcgov-hl authored Dec 13, 2023
1 parent eb53b46 commit 0130e84
Show file tree
Hide file tree
Showing 10 changed files with 322 additions and 44 deletions.
94 changes: 51 additions & 43 deletions OFM.Infrastructure.WebAPI/Handlers/BatchOperationsHandlers.cs
Original file line number Diff line number Diff line change
@@ -1,66 +1,74 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using OFM.Infrastructure.WebAPI.Extensions;
using OFM.Infrastructure.WebAPI.Messages;
using OFM.Infrastructure.WebAPI.Services.AppUsers;
using OFM.Infrastructure.WebAPI.Services.Batches;
using OFM.Infrastructure.WebAPI.Services.D365WebApi;
using OFM.Infrastructure.WebAPI.Services.Processes;
using System.Runtime.InteropServices.JavaScript;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace OFM.Infrastructure.WebAPI.Handlers;

public static class BatchOperationsHandlers
{
/// <summary>
/// Batch Operation to perform multiple actions
/// </summary>
/// <param name="batchService"></param>
/// <param name="loggerFactory"></param>
/// <param name="jsonBody"></param>
/// <returns></returns>
/// <remarks>
/// Sample request:
///
/// POST api/batches
///
/// {
/// "batchTypeId":2,
/// "feature": "AccountManagement",
/// "function":"UserEdit",
/// "actionMode": "Update",
/// "scope": "Parent-Child",
/// "data":{
/// "contact":{
/// "ofm_first_name":"first",
/// "ofm_last_name": "last",
/// "entityNameSet":"contacts",
/// "entityID":"00000000-0000-0000-0000-000000000000",
/// "emailaddress1": "[email protected]",
/// "ofm_portal_role": "1,2,3,4",
/// "actionMode":"Update"
/// },
/// "ofm_bceid_facility":[
/// {"entityID":"00000000-0000-0000-0000-000000000000","ofm_portal_access":true,"entityNameSet":"ofm_bceid_facilities","actionMode":"Update"},
/// {"entityID":"00000000-0000-0000-0000-000000000000","ofm_portal_access":false,"entityNameSet":"ofm_bceid_facilities","actionMode":"Update"}
/// ]
/// }
/// }
/// </remarks>
public static async Task<Results<BadRequest<string>, ProblemHttpResult, Ok<JsonObject>>> BatchOperationsAsync(
HttpContext context,
ID365WebApiService d365WebApiService,
ID365AppUserService appUserService,
ID365BatchService batchService,
ILoggerFactory loggerFactory,
[FromBody] dynamic jsonBody,
Guid? callerObjectId)
{
[FromBody] dynamic jsonBody)
{
var logger = loggerFactory.CreateLogger(LogCategory.Batch);
using (logger.BeginScope("ScopeBatch:POST"))
{
if (jsonBody is null) return TypedResults.BadRequest("Invalid batch query.");
var jsonData = JsonSerializer.Serialize(jsonBody);
using (JsonDocument jsonDocument = JsonDocument.Parse(jsonData))
{
JsonElement root = jsonDocument.RootElement;
JsonElement batchTypeId = root.GetProperty("batchTypeId");
JsonElement data = root.GetProperty("data");
//Add validation here

//if (context.Request?.QueryString.Value?.IndexOf('&') > 0)
//{
// var filters = context.Request.QueryString.Value.Substring(context.Request.QueryString.Value.IndexOf('&') + 1);
// statement = $"{statement}?{filters}";
//}
var batchResult = await batchService.ExecuteAsync(jsonDocument, batchTypeId.GetInt16());

//logger.LogDebug(CustomLogEvents.Operations, "Creating record(s) with the statement {statement}", statement);

//List<HttpRequestMessage> createRequests = new() {
// new CreateRequest("tasks",new JsonObject(){
// {"subject","Task 1 in batch OFM" }
// }),
// new CreateRequest("tasks",new JsonObject(){
// {"subject","Task 2 in batch OFM" }
// }),
// new CreateRequest("tasks",new JsonObject(){
// {"subject","Task 3 in batch OFM" }
// })
//};

List<HttpRequestMessage> requests = new() {
new UpdateRequest(new EntityReference("tasks",new Guid("00000000-0000-0000-0000-000000000000")),new JsonObject(){
{"subject","Task 1 in batch OFM (Updated3)" }
}),
new UpdateRequest(new EntityReference("tasks",new Guid("00000000-0000-0000-0000-000000000000")),new JsonObject(){
{"subject","Task 2 in batch OFM (Updated3).BAD" }
}),
new UpdateRequest(new EntityReference("tasks",new Guid("00000000-0000-0000-0000-000000000000")),new JsonObject(){
{"subject","Task 3 in batch OFM (Updated3)" }
})
};

var batchResult = await d365WebApiService.SendBatchMessageAsync(appUserService.AZPortalAppUser, requests, callerObjectId);

logger.LogDebug(CustomLogEvent.Batch, "Batch operation completed with the result {result}", JsonValue.Create<BatchResult>(batchResult));

return TypedResults.Ok(batchResult.SimpleBatchResult);
return TypedResults.Ok(batchResult.ODProcessResult);
};
}
}
}
8 changes: 7 additions & 1 deletion OFM.Infrastructure.WebAPI/Handlers/DocumentsHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
namespace OFM.Infrastructure.WebAPI.Handlers;
public static class DocumentsHandlers
{
private static bool uniqueFileNames;

public static async Task<Results<BadRequest<string>, ProblemHttpResult, Ok<JsonObject>>> GetAsync(
ID365DocumentService documentService,
string documentId)
Expand Down Expand Up @@ -70,8 +72,12 @@ public static async Task<Results<ProblemHttpResult, BadRequest<string>, Ok<Proce
using (logger.BeginScope("ScopeDocument"))
{
if (fileMapping.Length == 0 || !files.Any()) { return TypedResults.BadRequest("Invalid Query."); }

var mappings = JsonSerializer.Deserialize<List<FileMapping>>(fileMapping)?.ToList();
if (mappings is null || !mappings.Any()) return TypedResults.BadRequest("The fileMapping is not valid.");

var hasUniqueNames = mappings.GroupBy(f => f.ofm_subject).Any(c => c.Count() != 1);
if (hasUniqueNames) return TypedResults.BadRequest("Each filename must be unique.");

var uploadResult = await documentService.UploadAsync(files, mappings!);

Expand Down
37 changes: 37 additions & 0 deletions OFM.Infrastructure.WebAPI/OFM.Infrastructure.WebAPI.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions OFM.Infrastructure.WebAPI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using OFM.Infrastructure.WebAPI.Extensions;
using OFM.Infrastructure.WebAPI.Models;
using OFM.Infrastructure.WebAPI.Services.AppUsers;
using OFM.Infrastructure.WebAPI.Services.Batches;
using OFM.Infrastructure.WebAPI.Services.D365WebApi;
using OFM.Infrastructure.WebAPI.Services.Documents;
using OFM.Infrastructure.WebAPI.Services.Processes;
Expand Down Expand Up @@ -49,6 +50,10 @@
services.AddScoped<D365Email>();
services.AddScoped<ID365BackgroundProcessHandler, D365BackgroundProcessHandler>();

services.AddScoped<ID365BatchService, D365BatchService>();
services.AddScoped<ID365BatchProvider, BatchProvider>();
services.AddScoped<ID365BatchProvider, ContactEditProvider>();

services.AddD365HttpClient(builder.Configuration);
services.AddMvcCore().AddApiExplorer();
services.AddAuthentication();
Expand Down
75 changes: 75 additions & 0 deletions OFM.Infrastructure.WebAPI/Services/Batches/BatchProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using OFM.Infrastructure.WebAPI.Messages;
using OFM.Infrastructure.WebAPI.Services.AppUsers;
using OFM.Infrastructure.WebAPI.Services.Batches;
using OFM.Infrastructure.WebAPI.Services.D365WebApi;
using OFM.Infrastructure.WebAPI.Services.Processes;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace OFM.Infrastructure.WebAPI.Services.Documents;

public class BatchProvider : ID365BatchProvider
{
public Int16 BatchTypeId { get => 100; }

public async Task<string> PrepareDataAsync(JsonDocument jsonDocument, ID365AppUserService appUserService, ID365WebApiService d365WebApiService)
{
throw new NotImplementedException();
}

public async Task<JsonObject> ExecuteAsync(JsonDocument document, ID365AppUserService appUserService, ID365WebApiService d365WebApiService)
{
JsonElement root = document.RootElement;

JsonElement data = root.GetProperty("data");
List<HttpRequestMessage> requests = [];
foreach (var jsonElement in data.EnumerateObject())
{
JsonObject jsonObject = [];
if (jsonElement.Name != "" && jsonElement.Value.ValueKind != JsonValueKind.Null && jsonElement.Value.ValueKind != JsonValueKind.Undefined)
{
var obj = jsonElement.Value;
if (jsonElement.Value.ValueKind == JsonValueKind.Object)
{
var jsonRequest = await ProcessObjectData(obj, jsonObject);
requests.Add(jsonRequest);
}
else if (jsonElement.Value.ValueKind == JsonValueKind.Array)
{
foreach (var load in obj.EnumerateArray())
{
jsonObject = [];
var req = await ProcessObjectData(load, jsonObject);
requests.Add(req);
}
}
}
}

var batchResult = await d365WebApiService.SendBatchMessageAsync(appUserService.AZPortalAppUser, requests, null);

if (batchResult.Errors.Any())
{
var sendBatchError = ProcessResult.Failure(batchResult.ProcessId, batchResult.Errors, batchResult.TotalProcessed, batchResult.TotalRecords);
return sendBatchError.SimpleProcessResult;
}
var result= ProcessResult.Success(batchResult.ProcessId, batchResult.TotalRecords);
//return await Task.FromResult<JsonObject>(new JsonObject());
return result.SimpleProcessResult;
}

private async Task<HttpRequestMessage> ProcessObjectData(JsonElement payload, JsonObject keyValuePairs)
{
foreach (var data in payload.EnumerateObject())
{
keyValuePairs.Add(data.Name, (JsonNode)data.Value.ToString());
}
var entityName = keyValuePairs["entityNameSet"];
var entityId = keyValuePairs["entityID"].ToString();
keyValuePairs.Remove("entityNameSet");
keyValuePairs.Remove("entityID");
keyValuePairs.Remove("actionMode");
var request = new UpdateRequest(new EntityReference(entityName.ToString(), new Guid(entityId)), keyValuePairs);
return request;
}
}
34 changes: 34 additions & 0 deletions OFM.Infrastructure.WebAPI/Services/Batches/BatchService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

using OFM.Infrastructure.WebAPI.Services.AppUsers;
using OFM.Infrastructure.WebAPI.Services.D365WebApi;
using OFM.Infrastructure.WebAPI.Services.Processes;
using System.Net;
using System.Net.Sockets;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace OFM.Infrastructure.WebAPI.Services.Batches;

public class D365BatchService : ID365BatchService
{
protected readonly ID365WebApiService _d365webapiservice;
private readonly IEnumerable<ID365BatchProvider> _providers;
private readonly ID365AppUserService _appUserService;

public D365BatchService(ID365AppUserService appUserService, ID365WebApiService service, IEnumerable<ID365BatchProvider> providers)
{
_d365webapiservice = service;
_providers = providers;
_appUserService = appUserService;
}

public async Task<ProcessResult> ExecuteAsync(JsonDocument jsonDocument, Int16 batchTypeId)
{
ID365BatchProvider provider = _providers.First(p => p.BatchTypeId == batchTypeId);

var result = await provider.ExecuteAsync(jsonDocument, _appUserService, _d365webapiservice);
//process the result and return

return await Task.FromResult<ProcessResult>(ProcessResult.ODCompleted());
}
}
76 changes: 76 additions & 0 deletions OFM.Infrastructure.WebAPI/Services/Batches/ContactEditProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using OFM.Infrastructure.WebAPI.Messages;
using OFM.Infrastructure.WebAPI.Models;
using OFM.Infrastructure.WebAPI.Services.AppUsers;
using OFM.Infrastructure.WebAPI.Services.Batches;
using OFM.Infrastructure.WebAPI.Services.D365WebApi;
using OFM.Infrastructure.WebAPI.Services.Processes;
using System.Text.Json;
using System.Text.Json.Nodes;

namespace OFM.Infrastructure.WebAPI.Services.Batches;

public class ContactEditProvider : ID365BatchProvider
{
public Int16 BatchTypeId { get => 101; }

public async Task<string> PrepareDataAsync(JsonDocument jsonDocument, ID365AppUserService appUserService, ID365WebApiService d365WebApiService)
{
throw new NotImplementedException();
}

public async Task<JsonObject> ExecuteAsync(JsonDocument document, ID365AppUserService appUserService, ID365WebApiService d365WebApiService)
{
JsonElement root = document.RootElement;

JsonElement data = root.GetProperty("data");
List<HttpRequestMessage> requests = [];
foreach (var jsonElement in data.EnumerateObject())
{
JsonObject jsonObject = [];
if (jsonElement.Name != "" && jsonElement.Value.ValueKind != JsonValueKind.Null && jsonElement.Value.ValueKind != JsonValueKind.Undefined)
{
var obj = jsonElement.Value;
if (jsonElement.Value.ValueKind == JsonValueKind.Object)
{
var jsonRequest = await ProcessObjectData(obj, jsonObject);
requests.Add(jsonRequest);
}
else if (jsonElement.Value.ValueKind == JsonValueKind.Array)
{
foreach (var load in obj.EnumerateArray())
{
jsonObject = [];
var req = await ProcessObjectData(load, jsonObject);
requests.Add(req);
}
}
}
}

var batchResult = await d365WebApiService.SendBatchMessageAsync(appUserService.AZPortalAppUser, requests, null);

if (batchResult.Errors.Any())
{
var sendBatchError = ProcessResult.Failure(batchResult.ProcessId, batchResult.Errors, batchResult.TotalProcessed, batchResult.TotalRecords);
return sendBatchError.SimpleProcessResult;
}
var result = ProcessResult.Success(batchResult.ProcessId, batchResult.TotalRecords);
//return await Task.FromResult<JsonObject>(new JsonObject());
return result.SimpleProcessResult;
}

private async Task<HttpRequestMessage> ProcessObjectData(JsonElement payload, JsonObject keyValuePairs)
{
foreach (var data in payload.EnumerateObject())
{
keyValuePairs.Add(data.Name, (JsonNode)data.Value.ToString());
}
var entityName = keyValuePairs["entityNameSet"];
var entityId = keyValuePairs["entityID"].ToString();
keyValuePairs.Remove("entityNameSet");
keyValuePairs.Remove("entityID");
keyValuePairs.Remove("actionMode");
var request = new UpdateRequest(new EntityReference(entityName.ToString(), new Guid(entityId)), keyValuePairs);
return request;
}
}
Loading

0 comments on commit 0130e84

Please sign in to comment.