Files
step3/api/QuestionsApi/Services/CcsPromotionCommand.cs
T

93 lines
4.3 KiB
C#

using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using QuestionsApi.Models;
using System.Text.Json;
namespace QuestionsApi.Services;
public static class CcsPromotionCommand
{
public static async Task<int> RunAsync(string[] args, TextWriter output)
{
var apply = args.Contains("--apply", StringComparer.OrdinalIgnoreCase);
var sourceIndex = Array.FindIndex(args, value => value.Equals("--source", StringComparison.OrdinalIgnoreCase));
var source = sourceIndex >= 0 && sourceIndex + 1 < args.Length
? args[sourceIndex + 1]
: Path.Combine(AppContext.BaseDirectory, "content", "ccs");
var files = File.Exists(source)
? new[] { source }
: Directory.Exists(source)
? Directory.EnumerateFiles(source, "*.json", SearchOption.AllDirectories).ToArray()
: [];
if (files.Length == 0)
{
await output.WriteLineAsync($"CCS content not found: {source}");
return 2;
}
var json = new JsonSerializerOptions(JsonSerializerDefaults.Web) { PropertyNameCaseInsensitive = true };
var request = new CcsImportRequest();
foreach (var file in files)
{
var parsed = JsonSerializer.Deserialize<CcsImportRequest>(await File.ReadAllTextAsync(file), json)
?? throw new InvalidDataException($"Could not parse CCS content: {file}");
request.Orders.AddRange(parsed.Orders);
request.Cases.AddRange(parsed.Cases);
}
var duplicateOrders = request.Orders.GroupBy(value => value.Code, StringComparer.OrdinalIgnoreCase)
.Where(value => value.Count() > 1).Select(value => value.Key).ToList();
if (duplicateOrders.Count > 0)
throw new InvalidDataException($"Duplicate CCS order codes: {string.Join(", ", duplicateOrders)}");
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables().Build();
var mongo = new MongoDatabaseProvider(configuration);
var orders = mongo.Database.GetCollection<CcsOrderDefinition>("CcsOrderCatalog");
var cases = mongo.Database.GetCollection<CcsCaseRevision>("CcsCaseRevisions");
var existingCodes = (await orders.Find(Builders<CcsOrderDefinition>.Filter.Empty)
.Project(value => value.Code).ToListAsync()).ToHashSet(StringComparer.OrdinalIgnoreCase);
var errors = CcsCaseValidator.ValidateImport(request, existingCodes);
if (errors.Count > 0)
{
foreach (var error in errors) await output.WriteLineAsync(error);
return 1;
}
await output.WriteLineAsync($"Validated {request.Cases.Count} CCS case(s) and {request.Orders.Count} order(s).");
if (!apply)
{
await output.WriteLineAsync("Dry run only. Pass --apply to publish.");
return 0;
}
foreach (var order in request.Orders)
{
order.Code = order.Code.Trim().ToLowerInvariant();
order.UpdatedUtc = DateTime.UtcNow;
await orders.ReplaceOneAsync(value => value.Code == order.Code, order, new ReplaceOptions { IsUpsert = true });
}
var created = 0;
var unchanged = 0;
foreach (var incoming in request.Cases)
{
var current = await cases.Find(value => value.ExternalId == incoming.ExternalId && value.IsCurrent)
.SortByDescending(value => value.Version).FirstOrDefaultAsync();
if (current is not null && CcsStore.CaseContentDocument(current).Equals(CcsStore.CaseContentDocument(incoming)))
{
unchanged++;
continue;
}
if (current is not null)
await cases.UpdateOneAsync(value => value.Id == current.Id,
Builders<CcsCaseRevision>.Update.Set(value => value.IsCurrent, false));
incoming.Id = null;
incoming.Version = (current?.Version ?? 0) + 1;
incoming.IsCurrent = true;
incoming.CreatedUtc = DateTime.UtcNow;
await cases.InsertOneAsync(incoming);
created++;
}
await output.WriteLineAsync($"Promotion complete: {created} created, {unchanged} unchanged.");
return 0;
}
}