diff --git a/src/KubeUI/Client/Cluster.cs b/src/KubeUI/Client/Cluster.cs index de7b1ae4..ef08d15c 100644 --- a/src/KubeUI/Client/Cluster.cs +++ b/src/KubeUI/Client/Cluster.cs @@ -320,7 +320,7 @@ public void Seed(Type type) await Dispatcher.UIThread.InvokeAsync(() => items[name] = item); break; case WatchEventType.Deleted: - await Dispatcher.UIThread.InvokeAsync(() => items.TryRemove(name, out var old)); + await Dispatcher.UIThread.InvokeAsync(() => items.Remove(name)); //todo Check if CRD and remove from menu etc break; case WatchEventType.Error: diff --git a/src/KubeUI/ViewModels/NavigationViewModel.cs b/src/KubeUI/ViewModels/NavigationViewModel.cs index 3e590476..9cf7edf5 100644 --- a/src/KubeUI/ViewModels/NavigationViewModel.cs +++ b/src/KubeUI/ViewModels/NavigationViewModel.cs @@ -99,7 +99,7 @@ private static object GetContext(Type type, object list, GroupApiVersionKind kin constructedType.GetProperty(nameof(ResourceListViewModel.Objects))?.SetValue(instance, list); constructedType.GetProperty(nameof(ResourceListViewModel.Kind))?.SetValue(instance, kind); - constructedType.GetMethod("Initialize")?.Invoke(instance, null); + constructedType.GetMethod(nameof(ResourceListViewModel.Initialize))?.Invoke(instance, null); return instance; } diff --git a/src/KubeUI/ViewModels/ResourceListViewModel.cs b/src/KubeUI/ViewModels/ResourceListViewModel.cs index 3b800e6e..05cbee5e 100644 --- a/src/KubeUI/ViewModels/ResourceListViewModel.cs +++ b/src/KubeUI/ViewModels/ResourceListViewModel.cs @@ -64,6 +64,9 @@ internal class DemoResourceListViewModel : ResourceListViewModel { public [ObservableProperty] private string _searchQuery; + [ObservableProperty] + private ResourceListViewDefinition _viewDefinitions; + private IDisposable _filter; public ResourceListViewModel() @@ -82,28 +85,96 @@ public void Initialize() DataGridObjects = filteredObjects; Namespaces = Cluster.GetObjectDictionary(); + + ViewDefinitions = GetViewDefinition(); } private void SetFilter() { + _filter.Dispose(); + + _filter = Objects.ToObservableChangeSet, KeyValuePair>() + .Filter(GenerateFilter()) + .Bind(out var filteredObjects) + .Subscribe(); + + DataGridObjects = filteredObjects; + } + + private Func, bool> GenerateFilter() + { + var param = Expression.Parameter(typeof(KeyValuePair), "p"); + + var key = Expression.PropertyOrField(param, "Key"); + + var value = Expression.PropertyOrField(param, "Value"); + + BinaryExpression? body = null; + + BinaryExpression? namespaceFilter = null; + + BinaryExpression? searchFilter = null; + if (SelectedNamespaces != null) { - if (SelectedNamespaces is ICollection) - { - throw new NotImplementedException(); - } - else if(SelectedNamespaces is V1Namespace @namespace) + namespaceFilter = Expression.Equal( + Expression.PropertyOrField(key, "Namespace"), + Expression.Constant(((V1Namespace)SelectedNamespaces).Name()) + ); + } + + if(!string.IsNullOrEmpty(SearchQuery)) + { + var method = typeof(string).GetMethod(nameof(string.IndexOf), [typeof(string), typeof(StringComparison)]); + + foreach (var query in SearchQuery.Split(' ')) { - _filter.Dispose(); + if (string.IsNullOrEmpty(query)) + { + continue; + } + + BinaryExpression? wordFilter = null; + + foreach (var column in ViewDefinitions.Columns) + { + var someValue = Expression.Constant(query, typeof(string)); - _filter = Objects.ToObservableChangeSet, KeyValuePair>() - .Filter(x => x.Key.Namespace == @namespace.Metadata.Name) - .Bind(out var filteredObjects) - .Subscribe(); + var colType = column.GetType(); + + var columnDisplay = (Func)colType.GetProperty(nameof(ResourceListViewDefinitionColumn.Display)).GetValue(column); + + columnDisplay ??= (Func)colType.GetProperty(nameof(ResourceListViewDefinitionColumn.Field)).GetValue(column); + + var funcCall = Expression.Call(Expression.Constant(columnDisplay), columnDisplay.GetType().GetMethod("Invoke"), value); + + var expression = Expression.GreaterThanOrEqual(Expression.Call(funcCall, method, someValue, Expression.Constant(StringComparison.OrdinalIgnoreCase)), Expression.Constant(0)); + + wordFilter = wordFilter == null ? expression : Expression.OrElse(wordFilter, expression); + } - DataGridObjects = filteredObjects; + searchFilter = searchFilter == null ? wordFilter : Expression.AndAlso(searchFilter, wordFilter); } } + + if (namespaceFilter != null && searchFilter == null) + { + body = namespaceFilter; + } + else if (namespaceFilter == null && searchFilter != null) + { + body = searchFilter; + } + else if (namespaceFilter != null && searchFilter != null) + { + body = Expression.AndAlso(namespaceFilter, searchFilter); + } + else + { + body = Expression.Equal(Expression.Constant(true), Expression.Constant(true)); + } + + return Expression.Lambda, bool>>(body, param).Compile(); } protected override void OnPropertyChanged(PropertyChangedEventArgs e) @@ -117,11 +188,11 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) if (e.PropertyName == nameof(SearchQuery)) { - //SetFilter(); + SetFilter(); } } - public ResourceListViewDefinition GetResourceListViewDefinition() where T : class, IKubernetesObject, new() + private ResourceListViewDefinition GetViewDefinition() where T : class, IKubernetesObject, new() { var resourceType = typeof(T); @@ -177,7 +248,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) new ResourceListViewDefinitionColumn() { Name = "Status", - Field = x => x.Status.Conditions.FirstOrDefault(x => x.Type == "Ready")?.Reason, + Field = x => x.Status.Conditions.FirstOrDefault(x => x.Type == "Ready")?.Reason ?? "", Width = nameof(DataGridLengthUnitType.SizeToHeader) }, new ResourceListViewDefinitionColumn() @@ -185,6 +256,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" } }; @@ -217,6 +289,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss"), Width = "80" } }; @@ -237,6 +310,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Containers", CustomControl = typeof(PodContainerCell), Field = x => x.Spec.Containers.Count + ((x.Spec.InitContainers?.Count) ?? 0), + Display = x => (x.Spec.Containers.Count + ((x.Spec.InitContainers?.Count) ?? 0)).ToString(), Width = nameof(DataGridLengthUnitType.SizeToCells) }, new ResourceListViewDefinitionColumn() @@ -249,13 +323,13 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) { Name = "Restarts", Field = x => x.Status.ContainerStatuses.Sum(x => x.RestartCount), - Display = x => x.Status?.ContainerStatuses?.Sum(x => x.RestartCount).ToString(), + Display = x => x.Status.ContainerStatuses?.Sum(x => x.RestartCount).ToString() ?? "0", Width = nameof(DataGridLengthUnitType.SizeToHeader) }, new ResourceListViewDefinitionColumn() { Name = "Controlled By", - Field = x => x.Metadata.OwnerReferences.FirstOrDefault()?.Name, + Field = x => x.Metadata.OwnerReferences.FirstOrDefault()?.Name ?? "", Width = nameof(DataGridLengthUnitType.SizeToHeader) }, new ResourceListViewDefinitionColumn() @@ -281,6 +355,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" } }; @@ -365,7 +440,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) new ResourceListViewDefinitionColumn() { Name = "Available", - Field = x => x.Status.Conditions.FirstOrDefault(x => x.Type == "Available").Status, + Field = x => x.Status.Conditions.FirstOrDefault(x => x.Type == "Available")?.Status ?? "", Width = nameof(DataGridLengthUnitType.SizeToHeader) }, new ResourceListViewDefinitionColumn() @@ -373,6 +448,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" } }; @@ -411,6 +487,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" } }; @@ -446,7 +523,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) new ResourceListViewDefinitionColumn() { Name = "Source", - Field = x => x.Source.Component, + Field = x => x.Source.Component ?? "", Width = "*" }, new ResourceListViewDefinitionColumn() @@ -461,6 +538,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Last Seen", CustomControl = typeof(LastSeenCell), Field = x => x.LastTimestamp, + Display = x => x.LastTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Sort = SortDirection.Descending, Width = "80" }, @@ -469,6 +547,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" }, }; @@ -625,6 +704,7 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) Name = "Age", CustomControl = typeof(AgeCell), Field = x => x.Metadata.CreationTimestamp, + Display = x => x.Metadata.CreationTimestamp?.ToString("yyyy-MM-dd HH:mm:ss") ?? "", Width = "80" }; diff --git a/src/KubeUI/Views/ResourceListView.axaml b/src/KubeUI/Views/ResourceListView.axaml index 1374d81b..047a5fdf 100644 --- a/src/KubeUI/Views/ResourceListView.axaml +++ b/src/KubeUI/Views/ResourceListView.axaml @@ -33,7 +33,7 @@ Grid.Column="0" Width="200" HorizontalAlignment="Right" - IsEnabled="False" + Text="{ReflectionBinding SearchQuery}" Watermark="Search" /> )DataContext!).GetResourceListViewDefinition(); + var definition = ((ResourceListViewModel)DataContext!).ViewDefinitions; Grid.Columns.Clear();