using AmpDAL.CustomCode.OpenApiIngestion; using AmpDAL.DTO.OpenApiIngestion; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using Microsoft.OpenApi; using System.Security.Cryptography; using System.Text; namespace AMPBLL.OpenApiIngestion; public class OpenApiIngestionBLL( IOpenApiIngestionDAL _dal, AutoNumber _autoNumber ) : IOpenApiIngestionBLL { public async Task IngestAsync( OpenApiIngestionRequestDTO request, LoginDTO login, CancellationToken ct) { // 1. Decode Base64 → raw JSON/YAML if (string.IsNullOrWhiteSpace(request.Base64Content)) throw new ArgumentException("Base64Content is required."); byte[] bytes = Convert.FromBase64String(request.Base64Content); string rawJson = Encoding.UTF8.GetString(bytes); // 2. Parse the OpenAPI document var result = OpenApiDocument.Parse(rawJson); var doc = result.Document; if (result.Diagnostic.Errors.Count > 0) throw new InvalidOperationException( $"Invalid OpenAPI document: {result.Diagnostic.Errors[0].Message}"); // 3. Guard: only Draft versions (VERSIONSTATUS = 0) may be updated int versionStatus = await _dal.GetVersionStatusAsync( request.ApiVersionId, login.ClientId, login, ct); // 4. Compute content hash string contentHash = ComputeHash(rawJson); // 5. Build parsed operation list var operations = ParseOperations(doc); // 6. Wipe old data (parameters before operations due to FK) await _dal.DeleteParametersByInterfaceAsync(request.ApiInterfaceId, login.ClientId, login, ct); await _dal.DeleteOperationsByInterfaceAsync(request.ApiInterfaceId, login.ClientId, login, ct); // 7. Stamp the interface record await _dal.UpdateInterfaceSpecAsync( request.ApiInterfaceId, login.ClientId, contentHash, login.UserId, login, ct); // 8. Insert operations and their parameters int totalParameters = 0; if (operations.Count > 0) { var opNumbers = await _autoNumber.GetAutoNumber(operations.Count, "APIOPERATION", login); for (int i = 0; i < operations.Count; i++) { int opId = opNumbers.StartNumber + i; long insertedOpId = await _dal.InsertOperationAsync( operations[i], opId, request.ApiId, request.ApiInterfaceId, login.ClientId, login.UserId, sortOrder: i + 1, login, ct); var parms = operations[i].Parameters; if (parms.Count > 0) { var paramNumbers = await _autoNumber.GetAutoNumber(parms.Count, "APIPARAMETER", login); for (int j = 0; j < parms.Count; j++) { int paramId = paramNumbers.StartNumber + j; await _dal.InsertParameterAsync( parms[j], paramId, insertedOpId, request.ApiId, login.ClientId, login.UserId, sortOrder: j + 1, login, ct); } totalParameters += parms.Count; } } } return new OpenApiIngestionResultDTO { ContentHash = contentHash, OperationCount = operations.Count, ParameterCount = totalParameters, Operations = operations }; } // ────────────────────────────────────────────────────────────── // Private helpers // ────────────────────────────────────────────────────────────── private static List ParseOperations( Microsoft.OpenApi.OpenApiDocument doc) { var operations = new List(); foreach (var pathItem in doc.Paths) { foreach (var opEntry in pathItem.Value.Operations) { string operationKey = !string.IsNullOrWhiteSpace(opEntry.Value.OperationId) ? opEntry.Value.OperationId : ComputeHash(opEntry.Key.ToString() + pathItem.Key)[..16]; var op = new ParsedOperationDTO { OperationKey = operationKey, HttpMethod = opEntry.Key.ToString().ToUpperInvariant(), RelativePath = pathItem.Key }; foreach (var p in opEntry.Value.Parameters) { op.Parameters.Add(new ParsedParameterDTO { Name = p.Name, InLocation = p.In?.ToString() ?? "Query", IsRequired = p.Required, DataType = p.Schema?.Type?.ToString().ToLowerInvariant() ?? "string" }); } operations.Add(op); } } return operations; } private static string ComputeHash(string input) { using var sha = SHA256.Create(); byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input)); return Convert.ToHexString(bytes); } }