Migrate remaining legacy operator workflows
This commit is contained in:
@@ -17,6 +17,7 @@ public sealed partial class MainWindow
|
||||
new(StringComparer.Ordinal);
|
||||
private IThemeCatalogPersistenceService? _themeCatalogPersistenceService;
|
||||
private IExpertCatalogPersistenceService? _expertCatalogPersistenceService;
|
||||
private IStockMasterIdentityValidationService? _stockMasterIdentityValidationService;
|
||||
private IOperatorCatalogSchemaValidationService? _operatorCatalogSchemaValidationService;
|
||||
private CancellationTokenSource? _operatorCatalogWriteCancellation;
|
||||
private string? _operatorCatalogInitializationError;
|
||||
@@ -56,6 +57,8 @@ public sealed partial class MainWindow
|
||||
_expertCatalogPersistenceService = new LegacyExpertCatalogPersistenceService(
|
||||
runtime.Executor,
|
||||
mutationExecutor);
|
||||
_stockMasterIdentityValidationService =
|
||||
new LegacyStockMasterIdentityValidationService(runtime.Executor);
|
||||
_operatorCatalogSchemaValidationService =
|
||||
new LegacyOperatorCatalogSchemaValidationService(runtime.Executor);
|
||||
}
|
||||
@@ -63,6 +66,7 @@ public sealed partial class MainWindow
|
||||
{
|
||||
_themeCatalogPersistenceService = null;
|
||||
_expertCatalogPersistenceService = null;
|
||||
_stockMasterIdentityValidationService = null;
|
||||
_operatorCatalogSchemaValidationService = null;
|
||||
_operatorCatalogInitializationError =
|
||||
"The theme/expert database boundary could not be initialized.";
|
||||
@@ -376,12 +380,19 @@ public sealed partial class MainWindow
|
||||
|
||||
InitializeOperatorCatalogRuntime();
|
||||
var service = _themeCatalogPersistenceService;
|
||||
var identityValidation = _stockMasterIdentityValidationService;
|
||||
await ExecuteOperatorCatalogWriteAsync(
|
||||
requestId,
|
||||
"theme",
|
||||
"create",
|
||||
definition.ThemeCode,
|
||||
service?.CanMutate == true,
|
||||
service?.CanMutate == true && identityValidation is not null,
|
||||
async cancellationToken =>
|
||||
{
|
||||
_ = await identityValidation!.ValidateThemeItemsAsync(
|
||||
definition.Items,
|
||||
cancellationToken);
|
||||
},
|
||||
cancellationToken => service!.CreateAsync(definition, cancellationToken));
|
||||
}
|
||||
|
||||
@@ -403,12 +414,19 @@ public sealed partial class MainWindow
|
||||
|
||||
InitializeOperatorCatalogRuntime();
|
||||
var service = _themeCatalogPersistenceService;
|
||||
var identityValidation = _stockMasterIdentityValidationService;
|
||||
await ExecuteOperatorCatalogWriteAsync(
|
||||
requestId,
|
||||
"theme",
|
||||
"replace",
|
||||
expectedIdentity.ThemeCode,
|
||||
service?.CanMutate == true,
|
||||
service?.CanMutate == true && identityValidation is not null,
|
||||
async cancellationToken =>
|
||||
{
|
||||
_ = await identityValidation!.ValidateThemeItemsAsync(
|
||||
items,
|
||||
cancellationToken);
|
||||
},
|
||||
cancellationToken => service!.ReplaceAsync(
|
||||
expectedIdentity,
|
||||
newTitle,
|
||||
@@ -438,6 +456,7 @@ public sealed partial class MainWindow
|
||||
"delete",
|
||||
identity.ThemeCode,
|
||||
service?.CanMutate == true,
|
||||
validationBody: null,
|
||||
cancellationToken => service!.DeleteAsync(identity, cancellationToken));
|
||||
}
|
||||
|
||||
@@ -457,12 +476,19 @@ public sealed partial class MainWindow
|
||||
|
||||
InitializeOperatorCatalogRuntime();
|
||||
var service = _expertCatalogPersistenceService;
|
||||
var identityValidation = _stockMasterIdentityValidationService;
|
||||
await ExecuteOperatorCatalogWriteAsync(
|
||||
requestId,
|
||||
"expert",
|
||||
"create",
|
||||
definition.Identity.ExpertCode,
|
||||
service?.CanMutate == true,
|
||||
service?.CanMutate == true && identityValidation is not null,
|
||||
async cancellationToken =>
|
||||
{
|
||||
_ = await identityValidation!.ValidateExpertRecommendationsAsync(
|
||||
definition.Recommendations,
|
||||
cancellationToken);
|
||||
},
|
||||
cancellationToken => service!.CreateAsync(definition, cancellationToken));
|
||||
}
|
||||
|
||||
@@ -484,12 +510,19 @@ public sealed partial class MainWindow
|
||||
|
||||
InitializeOperatorCatalogRuntime();
|
||||
var service = _expertCatalogPersistenceService;
|
||||
var identityValidation = _stockMasterIdentityValidationService;
|
||||
await ExecuteOperatorCatalogWriteAsync(
|
||||
requestId,
|
||||
"expert",
|
||||
"replace",
|
||||
expectedIdentity.ExpertCode,
|
||||
service?.CanMutate == true,
|
||||
service?.CanMutate == true && identityValidation is not null,
|
||||
async cancellationToken =>
|
||||
{
|
||||
_ = await identityValidation!.ValidateExpertRecommendationsAsync(
|
||||
recommendations,
|
||||
cancellationToken);
|
||||
},
|
||||
cancellationToken => service!.ReplaceAsync(
|
||||
expectedIdentity,
|
||||
newName,
|
||||
@@ -519,6 +552,7 @@ public sealed partial class MainWindow
|
||||
"delete",
|
||||
identity.ExpertCode,
|
||||
service?.CanMutate == true,
|
||||
validationBody: null,
|
||||
cancellationToken => service!.DeleteAsync(identity, cancellationToken));
|
||||
}
|
||||
|
||||
@@ -672,6 +706,7 @@ public sealed partial class MainWindow
|
||||
string operation,
|
||||
string identityCode,
|
||||
bool canMutate,
|
||||
Func<CancellationToken, Task>? validationBody,
|
||||
Func<CancellationToken, Task<OperatorCatalogMutationReceipt>> operationBody)
|
||||
{
|
||||
if (!canMutate)
|
||||
@@ -764,6 +799,30 @@ public sealed partial class MainWindow
|
||||
return;
|
||||
}
|
||||
|
||||
if (validationBody is not null)
|
||||
{
|
||||
await validationBody(requestToken);
|
||||
requestToken.ThrowIfCancellationRequested();
|
||||
if (!CanStartOperatorCatalogWrite())
|
||||
{
|
||||
PostOperatorCatalogPlayoutActive(
|
||||
requestId,
|
||||
entity,
|
||||
operation,
|
||||
identityCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (browserGeneration != Volatile.Read(ref _operatorCatalogBrowserGeneration) ||
|
||||
!ReferenceEquals(
|
||||
Volatile.Read(ref _operatorCatalogWriteCancellation),
|
||||
requestCancellation))
|
||||
{
|
||||
requestCancellation.Cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mutationDispatched = true;
|
||||
Interlocked.Exchange(ref _operatorCatalogWriteInFlight, 1);
|
||||
var receipt = await operationBody(requestToken);
|
||||
@@ -806,6 +865,31 @@ public sealed partial class MainWindow
|
||||
outcomeUnknown: false,
|
||||
requestCancellation);
|
||||
}
|
||||
catch (StockMasterIdentityDataException) when (!mutationDispatched)
|
||||
{
|
||||
PostOperatorCatalogWriteErrorIfCurrent(
|
||||
requestId,
|
||||
entity,
|
||||
operation,
|
||||
identityCode,
|
||||
"INVALID_DATA",
|
||||
"Every catalog stock must be selected from one current, unambiguous stock-master identity.",
|
||||
outcomeUnknown: false,
|
||||
requestCancellation);
|
||||
}
|
||||
catch (DatabaseOperationException) when (!mutationDispatched)
|
||||
{
|
||||
PostOperatorCatalogWriteErrorIfCurrent(
|
||||
requestId,
|
||||
entity,
|
||||
operation,
|
||||
identityCode,
|
||||
"DATABASE_ERROR",
|
||||
"The stock-master identity verification failed before the database transaction began.",
|
||||
outcomeUnknown: false,
|
||||
requestCancellation);
|
||||
_ = PostDatabaseStatusAsync(_lifetimeCancellation.Token);
|
||||
}
|
||||
catch (OperatorCatalogMutationException exception)
|
||||
{
|
||||
if (exception.OutcomeUnknown)
|
||||
@@ -1152,10 +1236,12 @@ public sealed partial class MainWindow
|
||||
|
||||
private bool CanMutateThemeCatalog() =>
|
||||
_themeCatalogPersistenceService?.CanMutate == true &&
|
||||
_stockMasterIdentityValidationService is not null &&
|
||||
!IsOperatorCatalogWriteQuarantined();
|
||||
|
||||
private bool CanMutateExpertCatalog() =>
|
||||
_expertCatalogPersistenceService?.CanMutate == true &&
|
||||
_stockMasterIdentityValidationService is not null &&
|
||||
!IsOperatorCatalogWriteQuarantined();
|
||||
|
||||
private bool IsOperatorCatalogWriteQuarantined() =>
|
||||
@@ -1224,6 +1310,7 @@ public sealed partial class MainWindow
|
||||
CancelRequest(Interlocked.Exchange(ref _operatorCatalogWriteCancellation, null));
|
||||
_themeCatalogPersistenceService = null;
|
||||
_expertCatalogPersistenceService = null;
|
||||
_stockMasterIdentityValidationService = null;
|
||||
_operatorCatalogSchemaValidationService = null;
|
||||
}
|
||||
|
||||
@@ -1378,7 +1465,7 @@ public sealed partial class MainWindow
|
||||
market == ThemeMarket.Krx && session != ThemeSession.NotApplicable ||
|
||||
market == ThemeMarket.Nxt &&
|
||||
session is not (ThemeSession.PreMarket or ThemeSession.AfterMarket) ||
|
||||
!TryGetOperatorCatalogCode(element, "themeCode", 8, out var code) ||
|
||||
!TryGetExistingThemeCatalogCode(element, "themeCode", out var code) ||
|
||||
!TryGetOperatorCatalogText(element, "themeTitle", 128, out var title) ||
|
||||
market == ThemeMarket.Nxt && title.Contains("(NXT)", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -1612,6 +1699,15 @@ public sealed partial class MainWindow
|
||||
return value.Length == length && value.All(char.IsAsciiDigit);
|
||||
}
|
||||
|
||||
private static bool TryGetExistingThemeCatalogCode(
|
||||
JsonElement payload,
|
||||
string propertyName,
|
||||
out string value)
|
||||
{
|
||||
value = GetString(payload, propertyName) ?? string.Empty;
|
||||
return value.Length is >= 3 and <= 8 && value.All(char.IsAsciiDigit);
|
||||
}
|
||||
|
||||
private static bool TryGetOperatorCatalogText(
|
||||
JsonElement payload,
|
||||
string propertyName,
|
||||
|
||||
Reference in New Issue
Block a user