@oneuptime/common 10.0.68 → 10.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/Models/DatabaseModels/KubernetesCluster.ts +5 -0
  2. package/Models/DatabaseModels/KubernetesResource.ts +19 -0
  3. package/Server/API/KubernetesResourceAPI.ts +2 -0
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.ts +17 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.ts +134 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  7. package/Server/Services/DatabaseService.ts +19 -4
  8. package/Server/Services/KubernetesResourceService.ts +323 -8
  9. package/Server/Types/Database/QueryHelper.ts +127 -0
  10. package/Server/Types/Database/QueryUtil.ts +244 -0
  11. package/Server/Utils/VM/VMRunner.ts +39 -22
  12. package/Types/BaseDatabase/EndsWith.ts +41 -0
  13. package/Types/BaseDatabase/IncludesAll.ts +45 -0
  14. package/Types/BaseDatabase/IncludesNone.ts +48 -0
  15. package/Types/BaseDatabase/NotContains.ts +41 -0
  16. package/Types/BaseDatabase/StartsWith.ts +41 -0
  17. package/Types/IsolatedVM/ReturnResult.ts +6 -0
  18. package/Types/JSON.ts +20 -0
  19. package/Types/Kubernetes/KubernetesInventoryExtractor.ts +15 -1
  20. package/Types/SerializableObjectDictionary.ts +10 -0
  21. package/UI/Components/Filters/BooleanFilter.tsx +1 -0
  22. package/UI/Components/Filters/DateFilter.tsx +212 -25
  23. package/UI/Components/Filters/DropdownFilter.tsx +1 -0
  24. package/UI/Components/Filters/EntityFilter.tsx +214 -41
  25. package/UI/Components/Filters/FilterViewer.tsx +228 -146
  26. package/UI/Components/Filters/FilterViewerItem.tsx +1 -11
  27. package/UI/Components/Filters/FiltersForm.tsx +148 -97
  28. package/UI/Components/Filters/NumberFilter.tsx +219 -34
  29. package/UI/Components/Filters/OperatorSelector.tsx +91 -0
  30. package/UI/Components/Filters/TextFilter.tsx +182 -71
  31. package/UI/Components/Filters/Types/FilterOperator.ts +73 -0
  32. package/UI/Components/ModelTable/BaseModelTable.tsx +8 -0
  33. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +7 -1
  34. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  35. package/build/dist/Models/DatabaseModels/KubernetesResource.js +20 -0
  36. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  37. package/build/dist/Server/API/KubernetesResourceAPI.js +2 -0
  38. package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -1
  39. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.js +12 -0
  40. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776865086264-MigrationName.js.map +1 -0
  41. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js +123 -0
  42. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776881254913-DedupeKubernetesClustersAndAddUniqueIndex.js.map +1 -0
  43. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  44. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  45. package/build/dist/Server/Services/DatabaseService.js +18 -4
  46. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  47. package/build/dist/Server/Services/KubernetesResourceService.js +204 -8
  48. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  49. package/build/dist/Server/Types/Database/QueryHelper.js +110 -0
  50. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  51. package/build/dist/Server/Types/Database/QueryUtil.js +180 -0
  52. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  53. package/build/dist/Server/Utils/VM/VMRunner.js +33 -19
  54. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  55. package/build/dist/Types/BaseDatabase/EndsWith.js +31 -0
  56. package/build/dist/Types/BaseDatabase/EndsWith.js.map +1 -0
  57. package/build/dist/Types/BaseDatabase/IncludesAll.js +34 -0
  58. package/build/dist/Types/BaseDatabase/IncludesAll.js.map +1 -0
  59. package/build/dist/Types/BaseDatabase/IncludesNone.js +34 -0
  60. package/build/dist/Types/BaseDatabase/IncludesNone.js.map +1 -0
  61. package/build/dist/Types/BaseDatabase/NotContains.js +31 -0
  62. package/build/dist/Types/BaseDatabase/NotContains.js.map +1 -0
  63. package/build/dist/Types/BaseDatabase/StartsWith.js +31 -0
  64. package/build/dist/Types/BaseDatabase/StartsWith.js.map +1 -0
  65. package/build/dist/Types/JSON.js +5 -0
  66. package/build/dist/Types/JSON.js.map +1 -1
  67. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +7 -1
  68. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -1
  69. package/build/dist/Types/SerializableObjectDictionary.js +10 -0
  70. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  71. package/build/dist/UI/Components/Filters/BooleanFilter.js +1 -1
  72. package/build/dist/UI/Components/Filters/BooleanFilter.js.map +1 -1
  73. package/build/dist/UI/Components/Filters/DateFilter.js +158 -14
  74. package/build/dist/UI/Components/Filters/DateFilter.js.map +1 -1
  75. package/build/dist/UI/Components/Filters/DropdownFilter.js +1 -1
  76. package/build/dist/UI/Components/Filters/DropdownFilter.js.map +1 -1
  77. package/build/dist/UI/Components/Filters/EntityFilter.js +174 -30
  78. package/build/dist/UI/Components/Filters/EntityFilter.js.map +1 -1
  79. package/build/dist/UI/Components/Filters/FilterViewer.js +188 -97
  80. package/build/dist/UI/Components/Filters/FilterViewer.js.map +1 -1
  81. package/build/dist/UI/Components/Filters/FilterViewerItem.js +1 -6
  82. package/build/dist/UI/Components/Filters/FilterViewerItem.js.map +1 -1
  83. package/build/dist/UI/Components/Filters/FiltersForm.js +46 -38
  84. package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
  85. package/build/dist/UI/Components/Filters/NumberFilter.js +165 -23
  86. package/build/dist/UI/Components/Filters/NumberFilter.js.map +1 -1
  87. package/build/dist/UI/Components/Filters/OperatorSelector.js +41 -0
  88. package/build/dist/UI/Components/Filters/OperatorSelector.js.map +1 -0
  89. package/build/dist/UI/Components/Filters/TextFilter.js +130 -53
  90. package/build/dist/UI/Components/Filters/TextFilter.js.map +1 -1
  91. package/build/dist/UI/Components/Filters/Types/FilterOperator.js +63 -0
  92. package/build/dist/UI/Components/Filters/Types/FilterOperator.js.map +1 -0
  93. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +7 -0
  94. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  95. package/package.json +1 -1
@@ -2,8 +2,15 @@ import Dropdown, { DropdownOption, DropdownValue } from "../Dropdown/Dropdown";
2
2
  import FieldType from "../Types/FieldType";
3
3
  import Filter from "./Types/Filter";
4
4
  import FilterData from "./Types/FilterData";
5
+ import FilterOperator from "./Types/FilterOperator";
6
+ import OperatorSelector from "./OperatorSelector";
5
7
  import GenericObject from "../../../Types/GenericObject";
6
- import React, { ReactElement } from "react";
8
+ import Includes from "../../../Types/BaseDatabase/Includes";
9
+ import IncludesAll from "../../../Types/BaseDatabase/IncludesAll";
10
+ import IncludesNone from "../../../Types/BaseDatabase/IncludesNone";
11
+ import IsNull from "../../../Types/BaseDatabase/IsNull";
12
+ import NotNull from "../../../Types/BaseDatabase/NotNull";
13
+ import React, { ReactElement, useEffect, useState } from "react";
7
14
 
8
15
  export interface ComponentProps<T extends GenericObject> {
9
16
  filter: Filter<T>;
@@ -15,56 +22,222 @@ type EntityFilterFunction = <T extends GenericObject>(
15
22
  props: ComponentProps<T>,
16
23
  ) => ReactElement;
17
24
 
25
+ const ENTITY_ARRAY_OPERATORS: Array<FilterOperator> = [
26
+ FilterOperator.HasAnyOf,
27
+ FilterOperator.HasAllOf,
28
+ FilterOperator.HasNoneOf,
29
+ FilterOperator.IsEmpty,
30
+ FilterOperator.IsNotEmpty,
31
+ ];
32
+
33
+ const ENTITY_OPERATORS: Array<FilterOperator> = [
34
+ FilterOperator.Is,
35
+ FilterOperator.IsNot,
36
+ FilterOperator.IsEmpty,
37
+ FilterOperator.IsNotEmpty,
38
+ ];
39
+
40
+ type EntityState = {
41
+ operator: FilterOperator;
42
+ values: Array<string>;
43
+ };
44
+
45
+ const detectArrayState = (rawValue: unknown): EntityState => {
46
+ if (rawValue instanceof IncludesAll) {
47
+ return {
48
+ operator: FilterOperator.HasAllOf,
49
+ values: (rawValue.values as Array<string>).map((v: string) => {
50
+ return v.toString();
51
+ }),
52
+ };
53
+ }
54
+ if (rawValue instanceof IncludesNone) {
55
+ return {
56
+ operator: FilterOperator.HasNoneOf,
57
+ values: (rawValue.values as Array<string>).map((v: string) => {
58
+ return v.toString();
59
+ }),
60
+ };
61
+ }
62
+ if (rawValue instanceof Includes) {
63
+ return {
64
+ operator: FilterOperator.HasAnyOf,
65
+ values: (rawValue.values as Array<string>).map((v: string) => {
66
+ return v.toString();
67
+ }),
68
+ };
69
+ }
70
+ if (Array.isArray(rawValue)) {
71
+ return {
72
+ operator: FilterOperator.HasAnyOf,
73
+ values: (rawValue as Array<string>).map((v: string) => {
74
+ return v.toString();
75
+ }),
76
+ };
77
+ }
78
+ if (rawValue instanceof IsNull) {
79
+ return { operator: FilterOperator.IsEmpty, values: [] };
80
+ }
81
+ if (rawValue instanceof NotNull) {
82
+ return { operator: FilterOperator.IsNotEmpty, values: [] };
83
+ }
84
+ return { operator: FilterOperator.HasAnyOf, values: [] };
85
+ };
86
+
87
+ const detectSingleState = (rawValue: unknown): EntityState => {
88
+ if (rawValue instanceof IsNull) {
89
+ return { operator: FilterOperator.IsEmpty, values: [] };
90
+ }
91
+ if (rawValue instanceof NotNull) {
92
+ return { operator: FilterOperator.IsNotEmpty, values: [] };
93
+ }
94
+ if (typeof rawValue === "string" && rawValue) {
95
+ return { operator: FilterOperator.Is, values: [rawValue] };
96
+ }
97
+ return { operator: FilterOperator.Is, values: [] };
98
+ };
99
+
100
+ const buildArrayValue = (state: EntityState): unknown => {
101
+ switch (state.operator) {
102
+ case FilterOperator.HasAllOf:
103
+ return state.values.length > 0 ? new IncludesAll(state.values) : undefined;
104
+ case FilterOperator.HasNoneOf:
105
+ return state.values.length > 0 ? new IncludesNone(state.values) : undefined;
106
+ case FilterOperator.HasAnyOf:
107
+ return state.values.length > 0 ? state.values : undefined;
108
+ case FilterOperator.IsEmpty:
109
+ return new IsNull();
110
+ case FilterOperator.IsNotEmpty:
111
+ return new NotNull();
112
+ default:
113
+ return undefined;
114
+ }
115
+ };
116
+
117
+ const buildSingleValue = (state: EntityState): unknown => {
118
+ switch (state.operator) {
119
+ case FilterOperator.Is:
120
+ return state.values[0] || undefined;
121
+ case FilterOperator.IsNot:
122
+ // Use IncludesNone with single-item array to represent "is not".
123
+ return state.values[0]
124
+ ? new IncludesNone([state.values[0]])
125
+ : undefined;
126
+ case FilterOperator.IsEmpty:
127
+ return new IsNull();
128
+ case FilterOperator.IsNotEmpty:
129
+ return new NotNull();
130
+ default:
131
+ return undefined;
132
+ }
133
+ };
134
+
18
135
  const EntityFilter: EntityFilterFunction = <T extends GenericObject>(
19
136
  props: ComponentProps<T>,
20
137
  ): ReactElement => {
21
138
  const filter: Filter<T> = props.filter;
22
- const filterData: FilterData<T> = { ...props.filterData };
139
+
140
+ if (
141
+ filter.type !== FieldType.Entity &&
142
+ filter.type !== FieldType.EntityArray
143
+ ) {
144
+ return <></>;
145
+ }
146
+
147
+ if (!filter.filterDropdownOptions) {
148
+ return <></>;
149
+ }
150
+
151
+ const isArray: boolean = filter.type === FieldType.EntityArray;
152
+ const detectedState: EntityState = isArray
153
+ ? detectArrayState(props.filterData[filter.key])
154
+ : detectSingleState(props.filterData[filter.key]);
155
+
156
+ // Hold the operator locally so the user's choice persists even when no
157
+ // values are selected yet (otherwise `buildArrayValue` would return
158
+ // undefined, filterData wouldn't carry the operator, and the next render
159
+ // would reset back to the default).
160
+ const [localOperator, setLocalOperator] = useState<FilterOperator>(
161
+ detectedState.operator,
162
+ );
163
+
164
+ // When the external filter data changes and can unambiguously tell us
165
+ // which operator it represents, sync local state.
166
+ useEffect(() => {
167
+ const raw: unknown = props.filterData[filter.key];
168
+ if (raw !== undefined && raw !== null) {
169
+ setLocalOperator(detectedState.operator);
170
+ }
171
+ // Intentionally only re-run when the underlying filter data reference
172
+ // changes — not on every detectedState re-computation.
173
+ // eslint-disable-next-line react-hooks/exhaustive-deps
174
+ }, [props.filterData[filter.key]]);
175
+
176
+ const operator: FilterOperator = localOperator;
177
+ const state: EntityState = { ...detectedState, operator };
178
+
179
+ const valuelessOperator: boolean =
180
+ operator === FilterOperator.IsEmpty ||
181
+ operator === FilterOperator.IsNotEmpty;
23
182
 
24
183
  const dropdownValues: Array<DropdownOption> =
25
- props.filter.filterDropdownOptions?.filter((option: DropdownOption) => {
26
- if (filterData[filter.key] instanceof Array) {
27
- return (filterData[filter.key] as Array<string>)
28
- .map((value: string) => {
29
- return value.toString();
30
- })
31
- .includes(option.value.toString());
32
- }
33
-
34
- return option.value.toString() === filterData[filter.key]?.toString();
184
+ filter.filterDropdownOptions?.filter((option: DropdownOption) => {
185
+ return state.values.includes(option.value.toString());
35
186
  }) || [];
36
187
 
37
- if (
38
- (filter.type === FieldType.Entity ||
39
- filter.type === FieldType.EntityArray) &&
40
- filter.filterDropdownOptions
41
- ) {
42
- return (
43
- <Dropdown
44
- options={filter.filterDropdownOptions}
45
- onChange={(value: DropdownValue | Array<DropdownValue> | null) => {
46
- if (!filter.key) {
47
- return;
48
- }
49
-
50
- if (!value || (Array.isArray(value) && value.length === 0)) {
51
- delete filterData[filter.key];
52
- } else {
53
- filterData[filter.key] = value;
54
- }
55
-
56
- if (props.onFilterChanged) {
57
- props.onFilterChanged(filterData);
58
- }
188
+ type ApplyFunction = (nextState: EntityState) => void;
189
+
190
+ const apply: ApplyFunction = (nextState: EntityState): void => {
191
+ if (!filter.key) {
192
+ return;
193
+ }
194
+ setLocalOperator(nextState.operator);
195
+ const next: FilterData<T> = { ...props.filterData };
196
+ const built: unknown = isArray
197
+ ? buildArrayValue(nextState)
198
+ : buildSingleValue(nextState);
199
+ if (built === undefined) {
200
+ delete next[filter.key];
201
+ } else {
202
+ next[filter.key] = built as any;
203
+ }
204
+ props.onFilterChanged?.(next);
205
+ };
206
+
207
+ return (
208
+ <div className="flex gap-2 items-start">
209
+ <OperatorSelector
210
+ value={operator}
211
+ options={isArray ? ENTITY_ARRAY_OPERATORS : ENTITY_OPERATORS}
212
+ onChange={(nextOperator: FilterOperator) => {
213
+ apply({ ...state, operator: nextOperator });
59
214
  }}
60
- value={dropdownValues}
61
- isMultiSelect={filter.type === FieldType.EntityArray}
62
- placeholder={`Filter by ${filter.title}`}
63
215
  />
64
- );
65
- }
66
-
67
- return <></>;
216
+ {!valuelessOperator && (
217
+ <div className="flex-1 min-w-0">
218
+ <Dropdown
219
+ options={filter.filterDropdownOptions}
220
+ onChange={(value: DropdownValue | Array<DropdownValue> | null) => {
221
+ if (!value || (Array.isArray(value) && value.length === 0)) {
222
+ apply({ ...state, values: [] });
223
+ return;
224
+ }
225
+ const nextValues: Array<string> = Array.isArray(value)
226
+ ? (value as Array<DropdownValue>).map((v: DropdownValue) => {
227
+ return v.toString();
228
+ })
229
+ : [value.toString()];
230
+ apply({ ...state, values: nextValues });
231
+ }}
232
+ value={dropdownValues}
233
+ isMultiSelect={isArray}
234
+ placeholder={`Filter by ${filter.title}`}
235
+ className="relative rounded-md w-full overflow-visible"
236
+ />
237
+ </div>
238
+ )}
239
+ </div>
240
+ );
68
241
  };
69
242
 
70
243
  export default EntityFilter;