@page "/admin/commands" @page "/admin/commands/{DatabaseStr}/{CollectionStr}" @page "/admin/commands/{DatabaseStr}/{DatabaseInstanceStr}/{CollectionStr}" @attribute [Authorize] @using System.Reflection @using Rms.Risk.Mango.Components.Commands @inject NavigationManager NavigationManager @inject IUserSession UserSession @inject IJSRuntime JsRuntime @* * 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. *@

Commands

@foreach (var (group, controls) in _availableCommands) { @foreach (var (name, control) in controls) { var parameters = new Dictionary() { ["Collection"] = SelectedCollection, ["Parameters"] = _commandParams[name] }; } } @if (!string.IsNullOrWhiteSpace(Error)) {
@Error
}
@code { [CascadingParameter] public IModalService Modal { get; set; } = null!; [Parameter] public string? DatabaseStr { get; set; } [Parameter] public string? DatabaseInstanceStr { get; set; } [Parameter] public string? CollectionStr { get; set; } private IReadOnlyCollection Collections { get; set; } = []; private string SelectedCollection { get => UserSession.Collection; set { if (UserSession.Collection == value) return; UserSession.Collection = value; SyncUrl(); Error = ""; InvokeAsync(StateHasChanged); } } private string Database { get => UserSession.Database; set { if (UserSession.Database == value) return; UserSession.Database = value; SyncUrl(); } } private string DatabaseInstance { get => UserSession.DatabaseInstance; set { if (UserSession.DatabaseInstance == value) return; UserSession.DatabaseInstance = value; SyncUrl(); } } private string SelectedCommand { get; set { if (field == value || value == null || !_commandParams.ContainsKey(value)) return; field = value; Error = ""; InvokeAsync(StateHasChanged); } } = "Statistics"; private static string[] _groupsOrder = [ "Informational", "Admin", "Collections", "Indexes", "Documents" ]; private string Timeout { get; set; } = "20"; private string Error { get; set; } = ""; private bool IsReady { get; set; } private bool IsReadyToRun => IsReady && CanExecute && !string.IsNullOrWhiteSpace(CommandJson); private EditContext? _editContext; private EditContext EditContext => _editContext!; private string CommandName => _commandParams[SelectedCommand].Name; private string CommandJson => _commandParams[SelectedCommand].CommandJson; private bool CanExecute => _commandParams[SelectedCommand].CanExecute; private bool NeedConfirmation => _commandParams[SelectedCommand].NeedConfirmation; private CmdBase.CommandParams.DatabaseConnectionType RequiredConnectionType => _commandParams[SelectedCommand].RequiredConnectionType; private static readonly List<(string Group, List<(string Name, Type Control)> Controls)> _availableCommands; private Dictionary _commandParams = null!; private readonly Dictionary _shardConnectionStrings = []; private string Shard { get; set; } = ""; private IReadOnlyCollection Shards => _shardConnectionStrings.Keys.OrderBy(x => x).ToList(); private List Result { get => _commandParams[SelectedCommand].Result; set => _commandParams[SelectedCommand].Result = value; } static AdminCommands() { var types = typeof(AdminCommands).Assembly.GetTypes() .Select(x => { var attr = x.GetCustomAttribute(typeof(MongoCommandAttribute)) as MongoCommandAttribute; return (Type: x, Attr: attr); }) .Where(x => x.Attr != null) .GroupBy(x => x.Attr!.Group) .Select(x => (Group: x.Key, Commands: x.OrderBy(y => y.Attr!.Name).Select(y => (y.Attr!.Name, Control: y.Type) ).ToList())) .Where(x => _groupsOrder.Contains(x.Group)) .OrderBy(x => Array.IndexOf(_groupsOrder, x.Group)) .ToList() ; _availableCommands = types; } protected override void OnInitialized() { _editContext = new(this); _commandParams = new( _availableCommands .SelectMany(x => x.Controls) .Select(x => new KeyValuePair(x.Name, new (x.Name, OnChanged)) ) ); } private void OnChanged(CmdBase.CommandParams arg) { InvokeAsync(StateHasChanged); } protected override void OnAfterRender(bool firstRender) { if (!firstRender) return; if (string.IsNullOrWhiteSpace(DatabaseStr)) DatabaseStr = Database; else Database = DatabaseStr; if (string.IsNullOrWhiteSpace(DatabaseInstanceStr)) DatabaseInstanceStr = DatabaseInstance; else DatabaseInstance = DatabaseInstanceStr; if (string.IsNullOrWhiteSpace(CollectionStr)) CollectionStr = SelectedCollection; else SelectedCollection = CollectionStr; SyncUrl(); _ = Task.Run(async () => { try { await Task.WhenAll( LoadCollections(), LoadShards() ); IsReady = true; await InvokeAsync(StateHasChanged); } catch (Exception e) { await Display(e); } }); SyncUrl(); StateHasChanged(); } private async Task LoadCollections() { var admin = UserSession.MongoDbAdmin; Collections = await admin.ListCollections(); if (!Collections.Contains(SelectedCollection)) SelectedCollection = Collections.FirstOrDefault() ?? ""; } private async Task LoadShards() { var shards = await Shell.LoadShards(UserSession); _shardConnectionStrings.Clear(); foreach (var shard in shards) { _shardConnectionStrings.Add(shard.Key, shard.Value); } } private Task Display(Exception e) { Error = e.ToString(); return InvokeAsync(StateHasChanged); } private Task Display(List res) { Result = res; return InvokeAsync(StateHasChanged); } private async Task> RunCommand(BsonDocument doc, CancellationToken token) { var service = RequiredConnectionType switch { CmdBase.CommandParams.DatabaseConnectionType.Admin => UserSession.MongoDbAdminForAdminDatabase, CmdBase.CommandParams.DatabaseConnectionType.Cluster => UserSession.MongoDbAdmin, CmdBase.CommandParams.DatabaseConnectionType.Shard => _shardConnectionStrings.Count == 0 ? UserSession.MongoDbAdmin : UserSession.GetShardConnection(_shardConnectionStrings[Shard ?? throw new("Shard is not selected")].Host, _shardConnectionStrings[Shard].Port), _ => throw new($"Unknown connection type {RequiredConnectionType}") }; var result = await service.RunCommand(doc, token); return [result]; } private void SyncUrl() { var url = NavigationManager.BaseUri + $"admin/commands/{Database}"; if ( !string.IsNullOrWhiteSpace(DatabaseInstance) ) url += $"/{DatabaseInstance}"; url += $"/{SelectedCollection}"; JsRuntime.InvokeAsync("DashboardUtils.ChangeUrl", url); } private Task CanExecuteCommand() => Shell.CanExecuteCommand(UserSession, InvokeAsync, Modal); private async Task Execute() { Error = ""; if (string.IsNullOrWhiteSpace(CommandJson)) return; if (NeedConfirmation) { var res = await ModalDialogUtils.ShowConfirmationDialog(Modal, "Confirmation", $"Are you sure executing command \"{CommandName}\"? Some command's effects are irreversible!"); if (!res.Confirmed) return; } var ticket = await CanExecuteCommand(); if (string.IsNullOrWhiteSpace(ticket)) return; var cts = new CancellationTokenSource(TimeSpan.FromSeconds(int.Parse(Timeout))); try { IsReady = false; await InvokeAsync(StateHasChanged); var doc = BsonDocument.Parse(CommandJson); Shell.UpdateComment(doc, ticket, UserSession.User.GetEmail()); var res = await RunCommand(doc, cts.Token); await Display(res); } catch (Exception e) { await Display(e); } finally { IsReady = true; await InvokeAsync(StateHasChanged); } } }