@oneuptime/common 10.0.69 → 10.0.71

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