dbMango/Rms.Risk.Mango/Components/TransformJobControl.razor
Alexander Shabarshov 2a7a24c9e7 Initial contribution
2025-11-03 14:43:26 +00:00

286 lines
9.3 KiB
Plaintext

@using Rms.Risk.Mango.Services.Context
@inject IUserSession UserSession
@inject IDatabaseConfigurationService DatabaseConfig
@*
* dbMango
*
* Copyright 2025 Deutsche Bank AG
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*@
<style>
.dbName {
color: yellow;
font-weight: bold;
}
.colName {
color: lightgreen;
}
.CodeMirror {
width: 600px;
max-width: 600px;
}
</style>
<EditForm EditContext="EditContext">
<div class="flex-stack-vertical">
<div class="flex-stack-horizontal">
<div class="flex-stack-vertical ml-3">
<div class="form-row">
<FormItemSelect Enabled="@IsReady" Class="mr-2" Name="From database:" @bind-Value="SourceDatabase" Values="@SourceDatabases" />
</div>
@if( UserSession.IsInstanceSelectionAllowed(SourceDatabase) )
{
<div class="form-row">
<FormItemSelect Enabled="@IsReady" Class="mr-2" Name="From database:" @bind-Value="SourceDatabaseInstance" Values="@SourceDatabaseInstances" />
</div>
}
<div class="form-row">
<FormItemSelect Enabled="@IsReady" Class="mr-2" Name="From collection:" @bind-Value="SourceCollection" Values="@SourceCollections" />
</div>
<div class="form-row">
<FormItemText Enabled="@IsReady" Class="mr-2" Name="To collection:" @bind-Value="DestCollection" Placeholder="Blank = src collection"/>
</div>
<div class="form-row">
<FormItemCheckBox Enabled="@IsReady" Class="mr-2" Name="Clear destination" @bind-Value="WipeDestination" />
</div>
<div class="form-row">
<FormItemCheckBox Enabled="@IsReady" Class="mr-2" Name="Update / Insert" @bind-Value="Upsert" />
</div>
<div class="form-row">
<FormItemText Enabled="@IsReady" Class="mr-2" Name="BatchSize" @bind-Value="BatchSize" InputType="number" />
</div>
</div>
<div class="flex-stack-vertical ml-3">
<FormCodeEditor Name="Find:" @bind-Text="Find" MediaType="application/json"/>
<FormCodeEditor Name="Transform:" @bind-Text="Transform" MediaType="application/json" />
</div>
</div>
<div class="text-wrap mt-3 mb-3 w-100">@JobDescription</div>
</div>
<div class="form-row modal-footer w-100 p-0">
<button class="btn btn-secondary" @onclick="@(()=>ModalInstance.CancelAsync())">Cancel</button>
<button class="btn btn-primary" @onclick="OnOK" disabled="@(!IsReadyToRun)">Run job</button>
</div>
</EditForm>
@code {
[CascadingParameter] public BlazoredModalInstance ModalInstance { get; set; } = null!;
[CascadingParameter] public IModalService Modal { get; set; } = null!;
private string SourceDatabase
{
get;
set
{
if (field == value)
return;
field = value;
if (!string.IsNullOrWhiteSpace(field))
InvokeAsync(SourceDatabaseChanged);
}
} = "";
private string SourceDatabaseInstance
{
get;
set
{
if (field == value)
return;
field = value;
if (!string.IsNullOrWhiteSpace(field))
InvokeAsync(SourceDatabaseChanged);
}
} = "";
private string SourceCollection
{
get;
set
{
if (field == value)
return;
field = value;
if (!string.IsNullOrWhiteSpace(field))
InvokeAsync(StateHasChanged);
}
} = "";
private string DestCollection
{
get;
set
{
if (field == value)
return;
field = value;
if (!string.IsNullOrWhiteSpace(field))
InvokeAsync(StateHasChanged);
}
} = "";
private string Find { get; set; } = "{ _id : { $ne : \"\" } }";
private string Transform { get; set; } = "";
private string DestDatabase => UserSession.Database;
private bool WipeDestination { get; set; }
private string BatchSize { get; set; } = "1000";
private bool Upsert { get; set; }
private bool IsReady { get; set; }
private bool IsReadyToRun => IsReady
&& !string.IsNullOrWhiteSpace(SourceCollection)
&& !string.IsNullOrWhiteSpace(EffectiveDestCollection)
&& (SourceDatabase != DestDatabase || SourceCollection != EffectiveDestCollection)
;
private string Error { get; set; } = "";
private EditContext EditContext => _editContext!;
private string EffectiveDestCollection => string.IsNullOrWhiteSpace(DestCollection) ? SourceCollection : DestCollection;
private Task SourceDatabaseChanged()
{
_ = Task.Run(LoadCollections);
return Task.CompletedTask;
}
private List<string> SourceDatabases => DatabaseConfig.Databases.Keys
.OrderBy(x => x)
.ToList()
;
//TODO: load on change
private List<string> SourceDatabaseInstances => [];
private MarkupString JobDescription => new(
$"Copy collection <span class=\"colName\">{SourceCollection}</span> from <span class=\"dbName\">{SourceDatabase}</span> to <span class=\"colName\">{EffectiveDestCollection}</span> of <span class=\"dbName\">{DestDatabase}</span>."
+ (!string.IsNullOrWhiteSpace(Transform) ? "<br> Applying documents transformation." : "<br> Without any documents transformation.")
+ (WipeDestination ? "<br> Destination collections will be cleared first." : "")
+ (Upsert ? "<br> Existing documents will be updated." : "")
);
private EditContext? _editContext;
protected override void OnInitialized()
{
_editContext = new(this);
}
protected override void OnAfterRender(bool firstRender)
{
if (!firstRender)
return;
if (SourceDatabases.Count == 0)
return;
SourceDatabase = SourceDatabases.First();
StateHasChanged();
}
private async Task LoadCollections()
{
try
{
IsReady = false;
await InvokeAsync(StateHasChanged);
if (string.IsNullOrWhiteSpace(SourceDatabase))
return;
var admin = UserSession.GetCustomAdmin(SourceDatabase, SourceDatabaseInstance);
SourceCollections = await admin.ListCollections();
SourceCollection = SourceCollections.FirstOrDefault() ?? "";
}
catch (Exception ex)
{
await Display(ex);
}
finally
{
IsReady = true;
await InvokeAsync(StateHasChanged);
}
}
private IReadOnlyCollection<string> SourceCollections { get; set; } = [];
private Task Display(Exception e)
{
Error = e.ToString();
return InvokeAsync(StateHasChanged);
}
private async Task OnOK()
{
try
{
var job = new MigrationJob()
{
SourceDatabase = SourceDatabase,
DestinationDatabase = DestDatabase,
Email = UserSession.User.GetEmail(),
Upsert = Upsert,
ClearDestinationBefore = WipeDestination,
BatchSize = int.Parse(BatchSize),
Status =
[
new MigrationJob.CollectionJob
{
SourceCollection = SourceCollection,
DestinationCollection = EffectiveDestCollection,
Filter = BsonDocument.Parse(Find),
Projection = string.IsNullOrWhiteSpace(Transform) ? null : BsonDocument.Parse(Transform)
}
]
};
await ModalInstance.CloseAsync(ModalResult.Ok(job));
}
catch (Exception e)
{
await ModalDialogUtils.ShowExceptionDialog(Modal, "Error", e);
}
}
public static Task<ModalResult> ShowDialog(IModalService service)
{
var parameters = new ModalParameters
{
};
var options = new ModalOptions
{
HideCloseButton = false,
DisableBackgroundCancel = true
};
var form = service.Show<TransformJobControl>("New transformation", parameters, options);
return form.Result;
}
}