@acorex-platform/module-platform-management-client 0.0.21
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.
- package/fesm2022/acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs +4599 -0
- package/fesm2022/acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-contracts.mjs +179 -0
- package/fesm2022/acorex-platform-module-platform-management-client-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs +1542 -0
- package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs +71 -0
- package/fesm2022/acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-customize-menu.command-2319iHsD.mjs +231 -0
- package/fesm2022/acorex-platform-module-platform-management-client-customize-menu.command-2319iHsD.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-data-list-pick-entity-item.command-CJVpqPmY.mjs +52 -0
- package/fesm2022/acorex-platform-module-platform-management-client-data-list-pick-entity-item.command-CJVpqPmY.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-entity-master-actions-for-ai.util-DmGyoy9o.mjs +55 -0
- package/fesm2022/acorex-platform-module-platform-management-client-entity-master-actions-for-ai.util-DmGyoy9o.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-execute-token.command-DNuHR-0y.mjs +152 -0
- package/fesm2022/acorex-platform-module-platform-management-client-execute-token.command-DNuHR-0y.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-catalog-scopes.query-ByhDTWE6.mjs +71 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-catalog-scopes.query-ByhDTWE6.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-editor-widgets.query-DITPMmbw.mjs +68 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-editor-widgets.query-DITPMmbw.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-platform-navigation-for-ai.query-2oHMITw-.mjs +194 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-platform-navigation-for-ai.query-2oHMITw-.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-registered-entities.query-DjStd3a8.mjs +136 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-registered-entities.query-DjStd3a8.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-registered-queries.query-C-IfkTel.mjs +122 -0
- package/fesm2022/acorex-platform-module-platform-management-client-list-registered-queries.query-C-IfkTel.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-menu-list.component-C5HH5-Dw.mjs +524 -0
- package/fesm2022/acorex-platform-module-platform-management-client-menu-list.component-C5HH5-Dw.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-charts.queries-CQ34R13-.mjs +216 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-charts.queries-CQ34R13-.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-data.util-BIkULtKk.mjs +81 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-data.util-BIkULtKk.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-kpi.queries-BkPdhVq_.mjs +137 -0
- package/fesm2022/acorex-platform-module-platform-management-client-platform-console-kpi.queries-BkPdhVq_.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-column.component-CYb2zOSj.mjs +45 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-column.component-CYb2zOSj.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-edit.component-BBwrzcXa.mjs +178 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-edit.component-BBwrzcXa.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-view.component-DBlZVz6u.mjs +45 -0
- package/fesm2022/acorex-platform-module-platform-management-client-rest-api-generator-widget-view.component-DBlZVz6u.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-platform-management-client.mjs +2 -0
- package/fesm2022/acorex-platform-module-platform-management-client.mjs.map +1 -0
- package/package.json +28 -0
- package/types/acorex-platform-module-platform-management-client-contracts.d.ts +258 -0
- package/types/acorex-platform-module-platform-management-client.d.ts +548 -0
|
@@ -0,0 +1,1542 @@
|
|
|
1
|
+
import { resolveLayoutBuilderDialogResult, AXPLayoutBuilderService } from '@acorex-platform/framework-client/layout/builder';
|
|
2
|
+
import { AXPEntityResolver } from '@acorex-platform/framework-client/layout/entity';
|
|
3
|
+
import { resolveEntityActionCommandInput, entityActionDataRecord } from '@acorex-platform/framework-shared/runtime';
|
|
4
|
+
import { AXToastService } from '@acorex/components/toast';
|
|
5
|
+
import { AXTranslationService } from '@acorex/core/translation';
|
|
6
|
+
import * as i0 from '@angular/core';
|
|
7
|
+
import { inject, Injectable } from '@angular/core';
|
|
8
|
+
import { AXPWidgetsList } from '@acorex-platform/framework-shared/core';
|
|
9
|
+
import { parsePropertyExposeEntries } from '@acorex-platform/framework-shared/entity';
|
|
10
|
+
import { C as CREATE_USER_QUERY_I18N, a as CREATE_USER_QUERY_WIZARD_NAME, b as CREATE_USER_QUERY_ENTITY_CATALOG } from './acorex-platform-module-platform-management-client-create-user-query.types-DdygAori.mjs';
|
|
11
|
+
import { n as normalizeRestApiOutput, c as createEmptyRestApiOutput, R as REST_API_GENERATOR_WIDGET_NAME } from './acorex-platform-module-platform-management-client-acorex-platform-module-platform-management-client-V_ao4WVM.mjs';
|
|
12
|
+
|
|
13
|
+
//#region ----- Imports -----
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region ----- Constants -----
|
|
16
|
+
const LOOKUP_WIDGET_TYPES = new Set(['lookup-editor', 'select-editor']);
|
|
17
|
+
const USER_QUERY_RELATED_DEPTH = 3;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region ----- Public API -----
|
|
20
|
+
function parseEntityFullName(fullName) {
|
|
21
|
+
const trimmed = fullName.trim();
|
|
22
|
+
const separator = trimmed.indexOf('.');
|
|
23
|
+
if (separator <= 0 || separator === trimmed.length - 1) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
module: trimmed.slice(0, separator),
|
|
28
|
+
name: trimmed.slice(separator + 1),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function resolvePickedEntityFullName(value) {
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
return value.trim();
|
|
34
|
+
}
|
|
35
|
+
if (value == null || typeof value !== 'object') {
|
|
36
|
+
return '';
|
|
37
|
+
}
|
|
38
|
+
const record = value;
|
|
39
|
+
const id = typeof record['id'] === 'string' ? record['id'].trim() : '';
|
|
40
|
+
if (id.includes('.')) {
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
const moduleName = typeof record['module_id'] === 'string' ? record['module_id'].trim() : '';
|
|
44
|
+
const name = typeof record['name'] === 'string' ? record['name'].trim() : '';
|
|
45
|
+
if (moduleName && name) {
|
|
46
|
+
return `${moduleName}.${name}`;
|
|
47
|
+
}
|
|
48
|
+
return id;
|
|
49
|
+
}
|
|
50
|
+
function collectUserQueryEntityFieldGroups(entity, relatedByName) {
|
|
51
|
+
return collectGroups(entity, relatedByName, '', new Set());
|
|
52
|
+
}
|
|
53
|
+
function flattenEntityFieldGroups(groups) {
|
|
54
|
+
const result = [];
|
|
55
|
+
const walk = (group) => {
|
|
56
|
+
result.push(group);
|
|
57
|
+
for (const child of group.children ?? []) {
|
|
58
|
+
walk(child);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
for (const group of groups) {
|
|
62
|
+
walk(group);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
function defaultSelectedFieldGroups(groups) {
|
|
67
|
+
const selected = {};
|
|
68
|
+
for (const group of flattenEntityFieldGroups(groups)) {
|
|
69
|
+
selected[group.id] = [];
|
|
70
|
+
}
|
|
71
|
+
for (const group of groups) {
|
|
72
|
+
if (group.kind !== 'relation') {
|
|
73
|
+
selected[group.id] = group.items.map((item) => item.path);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return selected;
|
|
77
|
+
}
|
|
78
|
+
function collectSelectedFieldGroupPaths(fieldGroups, groups) {
|
|
79
|
+
const paths = [];
|
|
80
|
+
const walk = (group) => {
|
|
81
|
+
paths.push(...normalizeSelectedFieldPaths(fieldGroups?.[group.id]));
|
|
82
|
+
for (const child of group.children ?? []) {
|
|
83
|
+
walk(child);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
for (const group of groups) {
|
|
87
|
+
walk(group);
|
|
88
|
+
}
|
|
89
|
+
return paths;
|
|
90
|
+
}
|
|
91
|
+
function withRequiredIdField(paths) {
|
|
92
|
+
const unique = [];
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
for (const path of paths) {
|
|
95
|
+
const trimmed = path.trim();
|
|
96
|
+
if (!trimmed || seen.has(trimmed)) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
seen.add(trimmed);
|
|
100
|
+
unique.push(trimmed);
|
|
101
|
+
}
|
|
102
|
+
if (!seen.has('id')) {
|
|
103
|
+
unique.unshift('id');
|
|
104
|
+
}
|
|
105
|
+
return unique;
|
|
106
|
+
}
|
|
107
|
+
function normalizeSelectedFieldPaths(value) {
|
|
108
|
+
if (!Array.isArray(value)) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
const paths = [];
|
|
112
|
+
for (const item of value) {
|
|
113
|
+
if (typeof item === 'string') {
|
|
114
|
+
paths.push(item);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (item != null && typeof item === 'object') {
|
|
118
|
+
const path = item.path;
|
|
119
|
+
if (typeof path === 'string') {
|
|
120
|
+
paths.push(path);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return paths;
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region ----- Group collection -----
|
|
128
|
+
function collectGroups(entity, relatedByName, pathPrefix, ancestry) {
|
|
129
|
+
const groups = [];
|
|
130
|
+
const { host, lookups } = collectHostAndLookups(entity, pathPrefix);
|
|
131
|
+
if (host.items.length) {
|
|
132
|
+
groups.push(host);
|
|
133
|
+
}
|
|
134
|
+
groups.push(...lookups);
|
|
135
|
+
const hostFullName = `${entity.module}.${entity.name}`;
|
|
136
|
+
const nextAncestry = new Set(ancestry);
|
|
137
|
+
nextAncestry.add(hostFullName);
|
|
138
|
+
for (const related of entity.relatedEntities ?? []) {
|
|
139
|
+
const group = collectRelatedGroup(related, relatedByName, pathPrefix, nextAncestry);
|
|
140
|
+
if (group) {
|
|
141
|
+
groups.push(group);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return groups;
|
|
145
|
+
}
|
|
146
|
+
function collectHostAndLookups(entity, pathPrefix, allowedNames) {
|
|
147
|
+
const hostItems = [];
|
|
148
|
+
const lookups = [];
|
|
149
|
+
for (const property of entity?.properties ?? []) {
|
|
150
|
+
const name = property.name?.trim();
|
|
151
|
+
if (!name || name.toLowerCase() === 'id') {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (allowedNames && !allowedNames.has(name)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (isLookupProperty(property)) {
|
|
158
|
+
const lookupGroup = collectLookupGroup(property, pathPrefix);
|
|
159
|
+
if (lookupGroup) {
|
|
160
|
+
lookups.push(lookupGroup);
|
|
161
|
+
}
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
hostItems.push({
|
|
165
|
+
path: joinPath(pathPrefix, name),
|
|
166
|
+
title: property.title,
|
|
167
|
+
fallback: name,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
host: {
|
|
172
|
+
id: pathPrefix ? `rel_${sanitizeGroupId(pathPrefix)}_fields` : 'host',
|
|
173
|
+
kind: pathPrefix ? 'relation' : 'host',
|
|
174
|
+
title: '',
|
|
175
|
+
fallback: 'fields',
|
|
176
|
+
items: hostItems,
|
|
177
|
+
},
|
|
178
|
+
lookups,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function collectLookupGroup(property, pathPrefix) {
|
|
182
|
+
const name = property.name?.trim();
|
|
183
|
+
if (!name) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const items = [
|
|
187
|
+
{
|
|
188
|
+
path: joinPath(pathPrefix, name),
|
|
189
|
+
title: property.title,
|
|
190
|
+
fallback: name,
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
const opts = property.schema?.interface?.options;
|
|
194
|
+
for (const entry of parsePropertyExposeEntries(opts)) {
|
|
195
|
+
const target = entry.target.trim();
|
|
196
|
+
if (!target || target === name) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
items.push({
|
|
200
|
+
path: joinPath(pathPrefix, target),
|
|
201
|
+
title: entry.source,
|
|
202
|
+
fallback: entry.source.trim() || target,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
id: `lookup_${sanitizeGroupId(joinPath(pathPrefix, name))}`,
|
|
207
|
+
kind: 'lookup',
|
|
208
|
+
title: property.title,
|
|
209
|
+
fallback: name,
|
|
210
|
+
items,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function collectRelatedGroup(related, relatedByName, pathPrefix, ancestry) {
|
|
214
|
+
if (related.hidden === true) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const entityName = related.entity?.trim();
|
|
218
|
+
if (!entityName || ancestry.has(entityName)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const relatedEntity = relatedByName.get(entityName);
|
|
222
|
+
const dataPath = related.persistence?.dataPath?.trim() || entityName.split('.').pop() || entityName;
|
|
223
|
+
const pathRoot = joinPath(pathPrefix, dataPath);
|
|
224
|
+
const allowed = resolveAllowedPropertyNames(related, relatedEntity);
|
|
225
|
+
const { host, lookups } = collectHostAndLookups(relatedEntity, pathRoot, allowed);
|
|
226
|
+
const childAncestry = new Set(ancestry);
|
|
227
|
+
childAncestry.add(entityName);
|
|
228
|
+
const children = [...lookups];
|
|
229
|
+
for (const nested of relatedEntity?.relatedEntities ?? []) {
|
|
230
|
+
const child = collectRelatedGroup(nested, relatedByName, pathRoot, childAncestry);
|
|
231
|
+
if (child) {
|
|
232
|
+
children.push(child);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!host.items.length && !children.length) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
id: `rel_${sanitizeGroupId(pathRoot)}`,
|
|
240
|
+
kind: 'relation',
|
|
241
|
+
title: related.title,
|
|
242
|
+
fallback: entityName,
|
|
243
|
+
items: host.items,
|
|
244
|
+
children: children.length ? children : undefined,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function resolveAllowedPropertyNames(related, relatedEntity) {
|
|
248
|
+
const excluded = new Set((related.excludeProperties ?? [])
|
|
249
|
+
.filter((name) => typeof name === 'string')
|
|
250
|
+
.map((name) => name.trim())
|
|
251
|
+
.filter(Boolean));
|
|
252
|
+
const fromConfig = Array.isArray(related.properties)
|
|
253
|
+
? related.properties.filter((name) => typeof name === 'string' && name.trim().length > 0)
|
|
254
|
+
: [];
|
|
255
|
+
if (fromConfig.length) {
|
|
256
|
+
return new Set(fromConfig.map((name) => name.trim()).filter((name) => name.toLowerCase() !== 'id' && !excluded.has(name)));
|
|
257
|
+
}
|
|
258
|
+
if (!excluded.size) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const names = (relatedEntity?.properties ?? [])
|
|
262
|
+
.map((property) => property.name?.trim() ?? '')
|
|
263
|
+
.filter((name) => name && name.toLowerCase() !== 'id' && !excluded.has(name));
|
|
264
|
+
return new Set(names);
|
|
265
|
+
}
|
|
266
|
+
function isLookupProperty(property) {
|
|
267
|
+
const iface = property.schema?.interface;
|
|
268
|
+
const opts = iface?.options;
|
|
269
|
+
const lookupEntity = typeof opts?.['entity'] === 'string' ? String(opts['entity']).trim() : '';
|
|
270
|
+
if (!lookupEntity) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
const widgetType = typeof iface?.type === 'string' ? iface.type : '';
|
|
274
|
+
if (LOOKUP_WIDGET_TYPES.has(widgetType)) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
return /(Ids|IDs|Id|ID)$/i.test(property.name);
|
|
278
|
+
}
|
|
279
|
+
function joinPath(prefix, segment) {
|
|
280
|
+
return prefix ? `${prefix}.${segment}` : segment;
|
|
281
|
+
}
|
|
282
|
+
function sanitizeGroupId(value) {
|
|
283
|
+
return value.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_|_$/g, '');
|
|
284
|
+
}
|
|
285
|
+
//#endregion
|
|
286
|
+
|
|
287
|
+
//#region ----- Imports -----
|
|
288
|
+
const ENTITY_WIZARD_BACK = 'back';
|
|
289
|
+
const API_WIZARD_BACK = 'back';
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region ----- Public API -----
|
|
292
|
+
async function openCreateUserQueryWizard(args) {
|
|
293
|
+
if (args.sourceKind === 'advanced') {
|
|
294
|
+
return openAdvancedPlaceholder(args);
|
|
295
|
+
}
|
|
296
|
+
if (args.sourceKind === 'entity') {
|
|
297
|
+
return openEntitySourceWizard(args);
|
|
298
|
+
}
|
|
299
|
+
if (args.sourceKind === 'api') {
|
|
300
|
+
return openApiSourceWizard(args);
|
|
301
|
+
}
|
|
302
|
+
const result = await args.layoutBuilder
|
|
303
|
+
.create()
|
|
304
|
+
.dialog((dialog) => {
|
|
305
|
+
configureSimpleSourceWizard(dialog, args);
|
|
306
|
+
})
|
|
307
|
+
.show();
|
|
308
|
+
return resolveLayoutBuilderDialogResult(result);
|
|
309
|
+
}
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region ----- Entity sub-steps -----
|
|
312
|
+
async function openEntitySourceWizard(args) {
|
|
313
|
+
let entityName = '';
|
|
314
|
+
let previousFields;
|
|
315
|
+
while (true) {
|
|
316
|
+
const picked = await openEntityPickDialog(args, entityName);
|
|
317
|
+
if (!picked) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const graph = await loadEntityFieldGraph(args, picked);
|
|
321
|
+
if (!graph) {
|
|
322
|
+
entityName = picked;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const selectedGroups = picked === entityName && previousFields != null ? previousFields : graph.defaultGroups;
|
|
326
|
+
const outcome = await openEntityFieldsDialog(args, picked, graph.groups, selectedGroups);
|
|
327
|
+
if (outcome === ENTITY_WIZARD_BACK) {
|
|
328
|
+
entityName = picked;
|
|
329
|
+
previousFields = selectedGroups;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
return outcome;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async function openEntityPickDialog(args, entityName) {
|
|
336
|
+
const result = await args.layoutBuilder
|
|
337
|
+
.create()
|
|
338
|
+
.dialog((dialog) => {
|
|
339
|
+
dialog
|
|
340
|
+
.setTitle(CREATE_USER_QUERY_I18N.titles.entity)
|
|
341
|
+
.setSize('lg')
|
|
342
|
+
.setCloseButton(true)
|
|
343
|
+
.setContext(createInitialContext('entity', entityName))
|
|
344
|
+
.content((flex) => {
|
|
345
|
+
flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
|
|
346
|
+
wizard
|
|
347
|
+
.name(CREATE_USER_QUERY_WIZARD_NAME)
|
|
348
|
+
.setLook('circular-icon')
|
|
349
|
+
.setLinear(true)
|
|
350
|
+
.setDirection('horizontal')
|
|
351
|
+
.setShowActions(false)
|
|
352
|
+
.step('entity', CREATE_USER_QUERY_I18N.steps.entity, (step) => {
|
|
353
|
+
step.setIcon('fa-light fa-database').content((layout) => {
|
|
354
|
+
fillEntityPickStep(layout);
|
|
355
|
+
});
|
|
356
|
+
})
|
|
357
|
+
.step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
|
|
358
|
+
step.setIcon('fa-light fa-diagram-project').content((layout) => {
|
|
359
|
+
fillHintStep(layout, CREATE_USER_QUERY_I18N.steps.fields, CREATE_USER_QUERY_I18N.placeholders.fieldsNext);
|
|
360
|
+
});
|
|
361
|
+
})
|
|
362
|
+
.step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
|
|
363
|
+
step.setIcon('fa-light fa-file-contract').content((layout) => {
|
|
364
|
+
fillContractPlaceholderStep(layout);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
})
|
|
369
|
+
.setActions((actions) => {
|
|
370
|
+
actions
|
|
371
|
+
.cancel('@general:actions.cancel.title')
|
|
372
|
+
.custom({
|
|
373
|
+
title: '@general:actions.next.title',
|
|
374
|
+
command: 'continue-to-fields',
|
|
375
|
+
icon: 'fa-regular fa-arrow-right',
|
|
376
|
+
color: 'primary',
|
|
377
|
+
hidden: '{{!api.getStatus().isFirst}}',
|
|
378
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
379
|
+
})
|
|
380
|
+
.custom({
|
|
381
|
+
title: '@general:actions.next.title',
|
|
382
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
|
|
383
|
+
icon: 'fa-regular fa-arrow-right',
|
|
384
|
+
color: 'primary',
|
|
385
|
+
hidden: '{{api.getStatus().isFirst || api.getStatus().isLast}}',
|
|
386
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
387
|
+
})
|
|
388
|
+
.custom({
|
|
389
|
+
title: '@general:actions.submit.title',
|
|
390
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
|
|
391
|
+
icon: 'fa-regular fa-check',
|
|
392
|
+
color: 'success',
|
|
393
|
+
hidden: '{{!api.getStatus().isLast}}',
|
|
394
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
395
|
+
});
|
|
396
|
+
})
|
|
397
|
+
.onAction(async (dialogRef) => {
|
|
398
|
+
if (dialogRef.action() === 'cancel') {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
const context = dialogRef.context() ?? null;
|
|
402
|
+
const name = resolvePickedEntityFullName(context?.source?.entity?.entityName);
|
|
403
|
+
if (!name) {
|
|
404
|
+
throw new Error(CREATE_USER_QUERY_I18N.errors.entityRequired);
|
|
405
|
+
}
|
|
406
|
+
return name;
|
|
407
|
+
});
|
|
408
|
+
})
|
|
409
|
+
.show();
|
|
410
|
+
return resolveLayoutBuilderDialogResult(result);
|
|
411
|
+
}
|
|
412
|
+
async function openEntityFieldsDialog(args, entityName, groups, selectedGroups) {
|
|
413
|
+
const result = await args.layoutBuilder
|
|
414
|
+
.create()
|
|
415
|
+
.dialog((dialog) => {
|
|
416
|
+
dialog
|
|
417
|
+
.setTitle(CREATE_USER_QUERY_I18N.titles.entity)
|
|
418
|
+
.setSize('xl')
|
|
419
|
+
.setCloseButton(true)
|
|
420
|
+
.setContext(createInitialContext('entity', entityName, [], selectedGroups))
|
|
421
|
+
.content((flex) => {
|
|
422
|
+
flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
|
|
423
|
+
wizard
|
|
424
|
+
.name(CREATE_USER_QUERY_WIZARD_NAME)
|
|
425
|
+
.setLook('circular-icon')
|
|
426
|
+
.setLinear(true)
|
|
427
|
+
.setDirection('horizontal')
|
|
428
|
+
.setShowActions(false)
|
|
429
|
+
.setGuards({
|
|
430
|
+
next: (payload) => canLeaveEntityFieldsStep(payload, args, groups),
|
|
431
|
+
})
|
|
432
|
+
.step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
|
|
433
|
+
step.setIcon('fa-light fa-diagram-project').content((layout) => {
|
|
434
|
+
fillEntityFieldsStep(layout, entityName, groups);
|
|
435
|
+
});
|
|
436
|
+
})
|
|
437
|
+
.step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
|
|
438
|
+
step.setIcon('fa-light fa-file-contract').content((layout) => {
|
|
439
|
+
fillContractPlaceholderStep(layout);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
})
|
|
444
|
+
.setActions((actions) => {
|
|
445
|
+
actions
|
|
446
|
+
.cancel('@general:actions.cancel.title')
|
|
447
|
+
.custom({
|
|
448
|
+
title: '@general:actions.previous.title',
|
|
449
|
+
command: 'back-to-entity',
|
|
450
|
+
icon: 'fa-regular fa-arrow-left',
|
|
451
|
+
color: 'primary',
|
|
452
|
+
position: 'prefix',
|
|
453
|
+
hidden: '{{!api.getStatus().isFirst}}',
|
|
454
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
455
|
+
})
|
|
456
|
+
.custom({
|
|
457
|
+
title: '@general:actions.previous.title',
|
|
458
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
|
|
459
|
+
icon: 'fa-regular fa-arrow-left',
|
|
460
|
+
color: 'primary',
|
|
461
|
+
position: 'prefix',
|
|
462
|
+
hidden: '{{api.getStatus().isFirst}}',
|
|
463
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
464
|
+
})
|
|
465
|
+
.custom({
|
|
466
|
+
title: '@general:actions.next.title',
|
|
467
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
|
|
468
|
+
icon: 'fa-regular fa-arrow-right',
|
|
469
|
+
color: 'primary',
|
|
470
|
+
hidden: '{{api.getStatus().isLast}}',
|
|
471
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
472
|
+
})
|
|
473
|
+
.custom({
|
|
474
|
+
title: '@general:actions.submit.title',
|
|
475
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
|
|
476
|
+
icon: 'fa-regular fa-check',
|
|
477
|
+
color: 'success',
|
|
478
|
+
hidden: '{{!api.getStatus().isLast}}',
|
|
479
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
480
|
+
});
|
|
481
|
+
})
|
|
482
|
+
.onAction(async (dialogRef) => {
|
|
483
|
+
if (dialogRef.action() === 'cancel') {
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
if (dialogRef.action() === 'back-to-entity') {
|
|
487
|
+
return ENTITY_WIZARD_BACK;
|
|
488
|
+
}
|
|
489
|
+
const context = dialogRef.context() ?? null;
|
|
490
|
+
if (!context) {
|
|
491
|
+
return null;
|
|
492
|
+
}
|
|
493
|
+
const fields = withRequiredIdField(collectSelectedFieldGroupPaths(context.source?.entity?.fieldGroups, groups));
|
|
494
|
+
if (countFieldGroupItems(groups) > 0 && fields.filter((path) => path !== 'id').length === 0) {
|
|
495
|
+
throw new Error(CREATE_USER_QUERY_I18N.errors.fieldsRequired);
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
...context,
|
|
499
|
+
source: {
|
|
500
|
+
...context.source,
|
|
501
|
+
kind: 'entity',
|
|
502
|
+
entity: {
|
|
503
|
+
entityName,
|
|
504
|
+
fields,
|
|
505
|
+
fieldGroups: context.source?.entity?.fieldGroups,
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
});
|
|
510
|
+
})
|
|
511
|
+
.show();
|
|
512
|
+
return resolveLayoutBuilderDialogResult(result);
|
|
513
|
+
}
|
|
514
|
+
async function loadEntityFieldGraph(args, entityName) {
|
|
515
|
+
const parsed = parseEntityFullName(entityName);
|
|
516
|
+
if (!parsed) {
|
|
517
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.entityLoadFailed);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const entity = await args.entityResolver.get(parsed.module, parsed.name);
|
|
521
|
+
if (!entity) {
|
|
522
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.entityLoadFailed);
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
const relatedByName = await loadRelatedEntityClosure(args.entityResolver, entity.relatedEntities ?? []);
|
|
526
|
+
const groups = collectUserQueryEntityFieldGroups(entity, relatedByName);
|
|
527
|
+
return {
|
|
528
|
+
groups: await toFieldGroupViews(groups, args.translationService),
|
|
529
|
+
defaultGroups: defaultSelectedFieldGroups(groups),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
async function loadRelatedEntityClosure(entityResolver, roots) {
|
|
533
|
+
const relatedByName = new Map();
|
|
534
|
+
let pending = [...new Set(roots.map((item) => item.entity?.trim() ?? '').filter(Boolean))];
|
|
535
|
+
for (let depth = 1; depth <= USER_QUERY_RELATED_DEPTH && pending.length > 0; depth += 1) {
|
|
536
|
+
const batch = pending.filter((name) => !relatedByName.has(name));
|
|
537
|
+
pending = [];
|
|
538
|
+
await Promise.all(batch.map(async (fullName) => {
|
|
539
|
+
const parsed = parseEntityFullName(fullName);
|
|
540
|
+
if (!parsed) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const definition = await entityResolver.get(parsed.module, parsed.name);
|
|
544
|
+
if (!definition) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
relatedByName.set(fullName, definition);
|
|
548
|
+
for (const nested of definition.relatedEntities ?? []) {
|
|
549
|
+
const nestedName = nested.entity?.trim();
|
|
550
|
+
if (nestedName && !relatedByName.has(nestedName)) {
|
|
551
|
+
pending.push(nestedName);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}));
|
|
555
|
+
}
|
|
556
|
+
return relatedByName;
|
|
557
|
+
}
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region ----- API sub-steps -----
|
|
560
|
+
async function openApiSourceWizard(args) {
|
|
561
|
+
let previous = createApiWizardContext();
|
|
562
|
+
while (true) {
|
|
563
|
+
const tested = await openApiSourceDialog(args, previous);
|
|
564
|
+
if (!tested) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
const groups = inferJsonFieldGroups(tested.source?.api?.lastTest?.body);
|
|
568
|
+
const views = await toFieldGroupViews(groups, args.translationService);
|
|
569
|
+
const selected = isSameApiRequest(previous, tested) && previous.source?.api?.fieldGroups
|
|
570
|
+
? previous.source.api.fieldGroups
|
|
571
|
+
: defaultHostFieldGroups(groups);
|
|
572
|
+
const outcome = await openApiFieldsDialog(args, tested, views, selected);
|
|
573
|
+
if (outcome === API_WIZARD_BACK) {
|
|
574
|
+
previous = tested;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
return outcome;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async function openApiSourceDialog(args, previous) {
|
|
581
|
+
const result = await args.layoutBuilder
|
|
582
|
+
.create()
|
|
583
|
+
.dialog((dialog) => {
|
|
584
|
+
dialog
|
|
585
|
+
.setTitle(CREATE_USER_QUERY_I18N.titles.api)
|
|
586
|
+
.setSize('xl')
|
|
587
|
+
.setCloseButton(true)
|
|
588
|
+
.setContext(previous)
|
|
589
|
+
.content((flex) => {
|
|
590
|
+
flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
|
|
591
|
+
wizard
|
|
592
|
+
.name(CREATE_USER_QUERY_WIZARD_NAME)
|
|
593
|
+
.setLook('circular-icon')
|
|
594
|
+
.setLinear(true)
|
|
595
|
+
.setDirection('horizontal')
|
|
596
|
+
.setShowActions(false)
|
|
597
|
+
.step('source', CREATE_USER_QUERY_I18N.steps.source, (step) => {
|
|
598
|
+
step.setIcon('fa-light fa-cloud-arrow-up').content((layout) => {
|
|
599
|
+
fillApiSourceStep(layout);
|
|
600
|
+
});
|
|
601
|
+
})
|
|
602
|
+
.step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
|
|
603
|
+
step.setIcon('fa-light fa-diagram-project').content((layout) => {
|
|
604
|
+
fillHintStep(layout, CREATE_USER_QUERY_I18N.steps.fields, CREATE_USER_QUERY_I18N.placeholders.apiFieldsNext);
|
|
605
|
+
});
|
|
606
|
+
})
|
|
607
|
+
.step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
|
|
608
|
+
step.setIcon('fa-light fa-file-contract').content((layout) => {
|
|
609
|
+
fillContractPlaceholderStep(layout);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
})
|
|
614
|
+
.setActions((actions) => {
|
|
615
|
+
actions
|
|
616
|
+
.cancel('@general:actions.cancel.title')
|
|
617
|
+
.custom({
|
|
618
|
+
title: '@general:actions.next.title',
|
|
619
|
+
command: 'continue-to-fields',
|
|
620
|
+
icon: 'fa-regular fa-arrow-right',
|
|
621
|
+
color: 'primary',
|
|
622
|
+
hidden: '{{!api.getStatus().isFirst}}',
|
|
623
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
624
|
+
})
|
|
625
|
+
.custom({
|
|
626
|
+
title: '@general:actions.next.title',
|
|
627
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
|
|
628
|
+
icon: 'fa-regular fa-arrow-right',
|
|
629
|
+
color: 'primary',
|
|
630
|
+
hidden: '{{api.getStatus().isFirst || api.getStatus().isLast}}',
|
|
631
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
632
|
+
})
|
|
633
|
+
.custom({
|
|
634
|
+
title: '@general:actions.submit.title',
|
|
635
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
|
|
636
|
+
icon: 'fa-regular fa-check',
|
|
637
|
+
color: 'success',
|
|
638
|
+
hidden: '{{!api.getStatus().isLast}}',
|
|
639
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
640
|
+
});
|
|
641
|
+
})
|
|
642
|
+
.onAction(async (dialogRef) => {
|
|
643
|
+
if (dialogRef.action() === 'cancel') {
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
646
|
+
const context = dialogRef.context() ?? null;
|
|
647
|
+
const request = normalizeRestApiOutput(context?.source?.api?.request);
|
|
648
|
+
if (!request.url) {
|
|
649
|
+
throw new Error(CREATE_USER_QUERY_I18N.errors.apiUrlRequired);
|
|
650
|
+
}
|
|
651
|
+
const lastTest = await runRestApiRequest(request);
|
|
652
|
+
if (!lastTest.ok) {
|
|
653
|
+
await showApiTestFailure(args, lastTest);
|
|
654
|
+
throw new Error(lastTest.status === 0 ? CREATE_USER_QUERY_I18N.errors.apiTestNetwork : CREATE_USER_QUERY_I18N.errors.apiTestFailed);
|
|
655
|
+
}
|
|
656
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.placeholders.apiTestOk, { status: String(lastTest.status) }, 'success');
|
|
657
|
+
return createApiWizardContext({
|
|
658
|
+
connectionId: context?.source?.api?.connectionId,
|
|
659
|
+
request,
|
|
660
|
+
lastTest,
|
|
661
|
+
fieldGroups: context?.source?.api?.fieldGroups,
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
})
|
|
665
|
+
.show();
|
|
666
|
+
return resolveLayoutBuilderDialogResult(result);
|
|
667
|
+
}
|
|
668
|
+
async function openApiFieldsDialog(args, tested, groups, selectedGroups) {
|
|
669
|
+
const result = await args.layoutBuilder
|
|
670
|
+
.create()
|
|
671
|
+
.dialog((dialog) => {
|
|
672
|
+
dialog
|
|
673
|
+
.setTitle(CREATE_USER_QUERY_I18N.titles.api)
|
|
674
|
+
.setSize('xl')
|
|
675
|
+
.setCloseButton(true)
|
|
676
|
+
.setContext(createApiWizardContext(tested.source?.api, selectedGroups))
|
|
677
|
+
.content((flex) => {
|
|
678
|
+
flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
|
|
679
|
+
wizard
|
|
680
|
+
.name(CREATE_USER_QUERY_WIZARD_NAME)
|
|
681
|
+
.setLook('circular-icon')
|
|
682
|
+
.setLinear(true)
|
|
683
|
+
.setDirection('horizontal')
|
|
684
|
+
.setShowActions(false)
|
|
685
|
+
.setGuards({
|
|
686
|
+
next: (payload) => canLeaveApiFieldsStep(payload, args, groups),
|
|
687
|
+
})
|
|
688
|
+
.step('fields', CREATE_USER_QUERY_I18N.steps.fields, (step) => {
|
|
689
|
+
step.setIcon('fa-light fa-diagram-project').content((layout) => {
|
|
690
|
+
fillApiFieldsStep(layout, groups);
|
|
691
|
+
});
|
|
692
|
+
})
|
|
693
|
+
.step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
|
|
694
|
+
step.setIcon('fa-light fa-file-contract').content((layout) => {
|
|
695
|
+
fillContractPlaceholderStep(layout);
|
|
696
|
+
});
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
})
|
|
700
|
+
.setActions((actions) => {
|
|
701
|
+
actions
|
|
702
|
+
.cancel('@general:actions.cancel.title')
|
|
703
|
+
.custom({
|
|
704
|
+
title: '@general:actions.previous.title',
|
|
705
|
+
command: 'back-to-source',
|
|
706
|
+
icon: 'fa-regular fa-arrow-left',
|
|
707
|
+
color: 'primary',
|
|
708
|
+
position: 'prefix',
|
|
709
|
+
hidden: '{{!api.getStatus().isFirst}}',
|
|
710
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
711
|
+
})
|
|
712
|
+
.custom({
|
|
713
|
+
title: '@general:actions.previous.title',
|
|
714
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
|
|
715
|
+
icon: 'fa-regular fa-arrow-left',
|
|
716
|
+
color: 'primary',
|
|
717
|
+
position: 'prefix',
|
|
718
|
+
hidden: '{{api.getStatus().isFirst}}',
|
|
719
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
720
|
+
})
|
|
721
|
+
.custom({
|
|
722
|
+
title: '@general:actions.next.title',
|
|
723
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
|
|
724
|
+
icon: 'fa-regular fa-arrow-right',
|
|
725
|
+
color: 'primary',
|
|
726
|
+
hidden: '{{api.getStatus().isLast}}',
|
|
727
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
728
|
+
})
|
|
729
|
+
.custom({
|
|
730
|
+
title: '@general:actions.submit.title',
|
|
731
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
|
|
732
|
+
icon: 'fa-regular fa-check',
|
|
733
|
+
color: 'success',
|
|
734
|
+
hidden: '{{!api.getStatus().isLast}}',
|
|
735
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
736
|
+
});
|
|
737
|
+
})
|
|
738
|
+
.onAction(async (dialogRef) => {
|
|
739
|
+
if (dialogRef.action() === 'cancel') {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
if (dialogRef.action() === 'back-to-source') {
|
|
743
|
+
return API_WIZARD_BACK;
|
|
744
|
+
}
|
|
745
|
+
const context = dialogRef.context() ?? null;
|
|
746
|
+
if (!context) {
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
const fields = collectSelectedFieldGroupPaths(context.source?.api?.fieldGroups, groups);
|
|
750
|
+
if (countFieldGroupItems(groups) > 0 && fields.length === 0) {
|
|
751
|
+
throw new Error(CREATE_USER_QUERY_I18N.errors.fieldsRequired);
|
|
752
|
+
}
|
|
753
|
+
return createApiWizardContext({
|
|
754
|
+
...tested.source?.api,
|
|
755
|
+
fields,
|
|
756
|
+
fieldGroups: context.source?.api?.fieldGroups,
|
|
757
|
+
});
|
|
758
|
+
});
|
|
759
|
+
})
|
|
760
|
+
.show();
|
|
761
|
+
return resolveLayoutBuilderDialogResult(result);
|
|
762
|
+
}
|
|
763
|
+
function fillApiFieldsStep(layout, groups) {
|
|
764
|
+
layout.flex((flex) => {
|
|
765
|
+
flex.setDirection('column').setGap('16px').setAlignItems('stretch');
|
|
766
|
+
flex.fieldset((fieldset) => {
|
|
767
|
+
fieldset
|
|
768
|
+
.setTitle(CREATE_USER_QUERY_I18N.fields.sample)
|
|
769
|
+
.setDescription(CREATE_USER_QUERY_I18N.placeholders.selectedApiHint)
|
|
770
|
+
.setIcon('fa-light fa-code')
|
|
771
|
+
.setCols(1)
|
|
772
|
+
.formField(CREATE_USER_QUERY_I18N.fields.status, (field) => {
|
|
773
|
+
field.path('source.api.lastTest.status').textBox().readonly(true);
|
|
774
|
+
})
|
|
775
|
+
.formField(CREATE_USER_QUERY_I18N.fields.sample, (field) => {
|
|
776
|
+
field.path('source.api.lastTest.body').customWidget('json-viewer', {}).layout(1);
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
for (const group of groups) {
|
|
780
|
+
renderFieldGroup(flex, group, 'source.api.fieldGroups');
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
async function canLeaveApiFieldsStep(payload, args, groups) {
|
|
785
|
+
if (payload.step?.id !== 'fields') {
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
if (countFieldGroupItems(groups) === 0) {
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
const context = (payload.context ?? {});
|
|
792
|
+
const selected = collectSelectedFieldGroupPaths(context.source?.api?.fieldGroups, groups);
|
|
793
|
+
if (selected.length > 0) {
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.fieldsRequired);
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
function createApiWizardContext(api, fieldGroups = {}) {
|
|
800
|
+
const base = createInitialContext('api');
|
|
801
|
+
return {
|
|
802
|
+
...base,
|
|
803
|
+
source: {
|
|
804
|
+
...base.source,
|
|
805
|
+
api: {
|
|
806
|
+
request: api?.request ?? createEmptyRestApiOutput(),
|
|
807
|
+
connectionId: api?.connectionId,
|
|
808
|
+
lastTest: api?.lastTest
|
|
809
|
+
? { ...api.lastTest, status: String(api.lastTest.status) }
|
|
810
|
+
: undefined,
|
|
811
|
+
fields: api?.fields ?? [],
|
|
812
|
+
fieldGroups: Object.keys(fieldGroups).length ? fieldGroups : (api?.fieldGroups ?? {}),
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function isSameApiRequest(left, right) {
|
|
818
|
+
return JSON.stringify(left.source?.api?.request) === JSON.stringify(right.source?.api?.request);
|
|
819
|
+
}
|
|
820
|
+
async function showApiTestFailure(args, lastTest) {
|
|
821
|
+
if (Number(lastTest.status) === 0) {
|
|
822
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.apiTestNetwork);
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.apiTestFailed, { status: String(lastTest.status) });
|
|
826
|
+
}
|
|
827
|
+
async function runRestApiRequest(value) {
|
|
828
|
+
const request = normalizeRestApiOutput(value);
|
|
829
|
+
const url = buildApiRequestUrl(request);
|
|
830
|
+
if (!url) {
|
|
831
|
+
return { ok: false, status: 0, body: null };
|
|
832
|
+
}
|
|
833
|
+
const headers = {};
|
|
834
|
+
if (request.headers['ContentType'].trim()) {
|
|
835
|
+
headers['Content-Type'] = request.headers['ContentType'].trim();
|
|
836
|
+
}
|
|
837
|
+
if (request.headers['Authorization'].trim()) {
|
|
838
|
+
headers['Authorization'] = request.headers['Authorization'].trim();
|
|
839
|
+
}
|
|
840
|
+
for (const [key, headerValue] of Object.entries(request.headers)) {
|
|
841
|
+
if (key === 'Authorization' || key === 'ContentType' || key === 'tempId') {
|
|
842
|
+
continue;
|
|
843
|
+
}
|
|
844
|
+
if (typeof headerValue === 'string' && headerValue.trim()) {
|
|
845
|
+
headers[key] = headerValue.trim();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
const response = await fetch(url, {
|
|
850
|
+
method: request.method,
|
|
851
|
+
headers,
|
|
852
|
+
body: request.method === 'GET' || !request.body.trim() ? undefined : request.body,
|
|
853
|
+
credentials: 'omit',
|
|
854
|
+
signal: AbortSignal.timeout(15000),
|
|
855
|
+
});
|
|
856
|
+
const text = (await response.text()).trim();
|
|
857
|
+
let body = text || null;
|
|
858
|
+
if (text) {
|
|
859
|
+
try {
|
|
860
|
+
body = JSON.parse(text);
|
|
861
|
+
}
|
|
862
|
+
catch {
|
|
863
|
+
body = text;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return {
|
|
867
|
+
ok: response.status >= 200 && response.status < 300,
|
|
868
|
+
status: response.status,
|
|
869
|
+
body,
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
return { ok: false, status: 0, body: null };
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function buildApiRequestUrl(request) {
|
|
877
|
+
const base = request.url.trim();
|
|
878
|
+
if (!base) {
|
|
879
|
+
return '';
|
|
880
|
+
}
|
|
881
|
+
const params = new URLSearchParams();
|
|
882
|
+
for (const query of request.queries ?? []) {
|
|
883
|
+
for (const [key, value] of Object.entries(query)) {
|
|
884
|
+
if (key === 'tempId' || !String(key).trim() || value == null || !String(value).trim()) {
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
params.append(key, String(value).trim());
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const search = params.toString();
|
|
891
|
+
if (!search) {
|
|
892
|
+
return base;
|
|
893
|
+
}
|
|
894
|
+
return base.includes('?') ? `${base}&${search}` : `${base}?${search}`;
|
|
895
|
+
}
|
|
896
|
+
function inferJsonFieldGroups(sample) {
|
|
897
|
+
const root = firstPlainObject(sample);
|
|
898
|
+
if (!root) {
|
|
899
|
+
return [];
|
|
900
|
+
}
|
|
901
|
+
const envelope = splitJsonEnvelope(root);
|
|
902
|
+
if (!envelope) {
|
|
903
|
+
return jsonGroupsFromObject(root, '', 0, true);
|
|
904
|
+
}
|
|
905
|
+
const groups = jsonGroupsFromObject(envelope.record, envelope.path, 0, true);
|
|
906
|
+
if (envelope.meta.length > 0) {
|
|
907
|
+
groups.push({
|
|
908
|
+
id: 'response',
|
|
909
|
+
kind: 'lookup',
|
|
910
|
+
title: CREATE_USER_QUERY_I18N.fields.response,
|
|
911
|
+
fallback: 'response',
|
|
912
|
+
items: envelope.meta,
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
return groups;
|
|
916
|
+
}
|
|
917
|
+
function splitJsonEnvelope(root) {
|
|
918
|
+
const payloadKeys = ['data', 'result', 'results', 'items', 'records', 'value', 'payload', 'content'];
|
|
919
|
+
const flagKeys = new Set(['ok', 'success', 'status', 'kind', 'error', 'code', 'message']);
|
|
920
|
+
const metaObjectKeys = new Set(['info', 'meta', 'pagination', 'support', 'links', 'page']);
|
|
921
|
+
const keys = Object.keys(root);
|
|
922
|
+
const namedPayload = payloadKeys.find((key) => root[key] != null);
|
|
923
|
+
const hasFlag = keys.some((key) => flagKeys.has(key) && isJsonScalar(root[key]));
|
|
924
|
+
let payloadKey = namedPayload;
|
|
925
|
+
if (payloadKey) {
|
|
926
|
+
const rest = keys.filter((key) => key !== payloadKey);
|
|
927
|
+
const restAreMeta = rest.every((key) => isJsonScalar(root[key]) || flagKeys.has(key) || metaObjectKeys.has(key));
|
|
928
|
+
if (!hasFlag && !restAreMeta) {
|
|
929
|
+
payloadKey = undefined;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
else {
|
|
933
|
+
const arrayKeys = keys.filter((key) => {
|
|
934
|
+
const value = root[key];
|
|
935
|
+
return Array.isArray(value) && value.some((item) => isPlainObject(item));
|
|
936
|
+
});
|
|
937
|
+
const otherKeys = keys.filter((key) => !arrayKeys.includes(key));
|
|
938
|
+
if (arrayKeys.length === 1 &&
|
|
939
|
+
otherKeys.length > 0 &&
|
|
940
|
+
otherKeys.every((key) => isJsonScalar(root[key]))) {
|
|
941
|
+
payloadKey = arrayKeys[0];
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (!payloadKey) {
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
const payload = root[payloadKey];
|
|
948
|
+
const meta = [];
|
|
949
|
+
for (const [key, value] of Object.entries(root)) {
|
|
950
|
+
if (key === payloadKey || !isJsonScalar(value)) {
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
meta.push({ path: key, fallback: key });
|
|
954
|
+
}
|
|
955
|
+
if (Array.isArray(payload)) {
|
|
956
|
+
const record = firstPlainObject(payload);
|
|
957
|
+
if (!record) {
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
return { record, path: payloadKey, meta };
|
|
961
|
+
}
|
|
962
|
+
if (isPlainObject(payload)) {
|
|
963
|
+
const items = payload['items'];
|
|
964
|
+
if (Array.isArray(items)) {
|
|
965
|
+
const record = firstPlainObject(items);
|
|
966
|
+
if (record) {
|
|
967
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
968
|
+
if (key === 'items' || !isJsonScalar(value)) {
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
meta.push({ path: `${payloadKey}.${key}`, fallback: key });
|
|
972
|
+
}
|
|
973
|
+
return { record, path: `${payloadKey}.items`, meta };
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return { record: payload, path: payloadKey, meta };
|
|
977
|
+
}
|
|
978
|
+
meta.push({ path: payloadKey, fallback: payloadKey });
|
|
979
|
+
return { record: { value: payload }, path: payloadKey, meta };
|
|
980
|
+
}
|
|
981
|
+
function jsonGroupsFromObject(value, prefix, depth, asHost = false) {
|
|
982
|
+
const scalars = [];
|
|
983
|
+
const nested = [];
|
|
984
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
985
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
986
|
+
const unwrapped = Array.isArray(raw) ? firstPlainObject(raw) : raw;
|
|
987
|
+
if (isPlainObject(unwrapped) && depth < 2) {
|
|
988
|
+
nested.push({ key, path, value: unwrapped });
|
|
989
|
+
continue;
|
|
990
|
+
}
|
|
991
|
+
scalars.push({ path, fallback: key });
|
|
992
|
+
}
|
|
993
|
+
const groups = [];
|
|
994
|
+
if (scalars.length > 0) {
|
|
995
|
+
const leaf = prefix.includes('.') ? (prefix.split('.').pop() ?? prefix) : prefix;
|
|
996
|
+
const isHost = (asHost && depth === 0) || !prefix;
|
|
997
|
+
groups.push({
|
|
998
|
+
id: isHost ? 'host' : `rel_${prefix.replace(/[^a-zA-Z0-9]+/g, '_')}_fields`,
|
|
999
|
+
kind: isHost ? 'host' : 'relation',
|
|
1000
|
+
title: leaf,
|
|
1001
|
+
fallback: leaf || 'fields',
|
|
1002
|
+
items: scalars,
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
if (depth >= 2) {
|
|
1006
|
+
return groups;
|
|
1007
|
+
}
|
|
1008
|
+
for (const item of nested) {
|
|
1009
|
+
const children = jsonGroupsFromObject(item.value, item.path, depth + 1, false);
|
|
1010
|
+
groups.push({
|
|
1011
|
+
id: `rel_${item.path.replace(/[^a-zA-Z0-9]+/g, '_')}`,
|
|
1012
|
+
kind: 'relation',
|
|
1013
|
+
title: item.key,
|
|
1014
|
+
fallback: item.key,
|
|
1015
|
+
items: [],
|
|
1016
|
+
children: children.length > 0 ? children : undefined,
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
return groups;
|
|
1020
|
+
}
|
|
1021
|
+
function defaultHostFieldGroups(groups) {
|
|
1022
|
+
const selected = {};
|
|
1023
|
+
const walk = (group) => {
|
|
1024
|
+
selected[group.id] = group.kind === 'host' ? group.items.map((item) => item.path) : [];
|
|
1025
|
+
for (const child of group.children ?? []) {
|
|
1026
|
+
walk(child);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
for (const group of groups) {
|
|
1030
|
+
walk(group);
|
|
1031
|
+
}
|
|
1032
|
+
return selected;
|
|
1033
|
+
}
|
|
1034
|
+
function firstPlainObject(value) {
|
|
1035
|
+
if (Array.isArray(value)) {
|
|
1036
|
+
for (const item of value) {
|
|
1037
|
+
const found = firstPlainObject(item);
|
|
1038
|
+
if (found) {
|
|
1039
|
+
return found;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return null;
|
|
1043
|
+
}
|
|
1044
|
+
return isPlainObject(value) ? value : null;
|
|
1045
|
+
}
|
|
1046
|
+
function isPlainObject(value) {
|
|
1047
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
1048
|
+
}
|
|
1049
|
+
function isJsonScalar(value) {
|
|
1050
|
+
if (value == null) {
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
const type = typeof value;
|
|
1054
|
+
return type === 'string' || type === 'number' || type === 'boolean';
|
|
1055
|
+
}
|
|
1056
|
+
//#endregion
|
|
1057
|
+
//#region ----- Advanced placeholder -----
|
|
1058
|
+
async function openAdvancedPlaceholder(args) {
|
|
1059
|
+
await args.layoutBuilder
|
|
1060
|
+
.create()
|
|
1061
|
+
.dialog((dialog) => {
|
|
1062
|
+
dialog
|
|
1063
|
+
.setTitle(CREATE_USER_QUERY_I18N.titles.advanced)
|
|
1064
|
+
.setMessage(CREATE_USER_QUERY_I18N.placeholders.advanced)
|
|
1065
|
+
.setSize('md')
|
|
1066
|
+
.setCloseButton(true)
|
|
1067
|
+
.setActions((actions) => {
|
|
1068
|
+
actions.cancel('@general:actions.close.title');
|
|
1069
|
+
})
|
|
1070
|
+
.onAction(async () => null);
|
|
1071
|
+
})
|
|
1072
|
+
.show();
|
|
1073
|
+
return null;
|
|
1074
|
+
}
|
|
1075
|
+
//#endregion
|
|
1076
|
+
//#region ----- Simple source wizard -----
|
|
1077
|
+
function configureSimpleSourceWizard(dialog, args) {
|
|
1078
|
+
dialog
|
|
1079
|
+
.setTitle(dialogTitleKey(args.sourceKind))
|
|
1080
|
+
.setSize(args.sourceKind === 'api' ? 'xl' : 'lg')
|
|
1081
|
+
.setCloseButton(true)
|
|
1082
|
+
.setContext(createInitialContext(args.sourceKind))
|
|
1083
|
+
.content((flex) => {
|
|
1084
|
+
flex.setDirection('column').setGap('16px').stepWizard((wizard) => {
|
|
1085
|
+
wizard
|
|
1086
|
+
.name(CREATE_USER_QUERY_WIZARD_NAME)
|
|
1087
|
+
.setLook('circular-icon')
|
|
1088
|
+
.setLinear(true)
|
|
1089
|
+
.setDirection('horizontal')
|
|
1090
|
+
.setShowActions(false)
|
|
1091
|
+
.setGuards({
|
|
1092
|
+
next: (payload) => canLeaveSourceStep(payload, args),
|
|
1093
|
+
})
|
|
1094
|
+
.step('source', CREATE_USER_QUERY_I18N.steps.source, (step) => {
|
|
1095
|
+
step.setIcon(sourceStepIcon(args.sourceKind)).content((layout) => {
|
|
1096
|
+
fillSourceStep(layout, args);
|
|
1097
|
+
});
|
|
1098
|
+
})
|
|
1099
|
+
.step('contract', CREATE_USER_QUERY_I18N.steps.contract, (step) => {
|
|
1100
|
+
step.setIcon('fa-light fa-file-contract').content((layout) => {
|
|
1101
|
+
fillContractPlaceholderStep(layout);
|
|
1102
|
+
});
|
|
1103
|
+
});
|
|
1104
|
+
});
|
|
1105
|
+
})
|
|
1106
|
+
.setActions((actions) => {
|
|
1107
|
+
actions
|
|
1108
|
+
.cancel('@general:actions.cancel.title')
|
|
1109
|
+
.custom({
|
|
1110
|
+
title: '@general:actions.previous.title',
|
|
1111
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.previous`,
|
|
1112
|
+
icon: 'fa-regular fa-arrow-left',
|
|
1113
|
+
color: 'primary',
|
|
1114
|
+
position: 'prefix',
|
|
1115
|
+
disabled: '{{api.getStatus().isFirst}}',
|
|
1116
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
1117
|
+
})
|
|
1118
|
+
.custom({
|
|
1119
|
+
title: '@general:actions.next.title',
|
|
1120
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.next`,
|
|
1121
|
+
icon: 'fa-regular fa-arrow-right',
|
|
1122
|
+
color: 'primary',
|
|
1123
|
+
hidden: '{{api.getStatus().isLast}}',
|
|
1124
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
1125
|
+
})
|
|
1126
|
+
.custom({
|
|
1127
|
+
title: '@general:actions.submit.title',
|
|
1128
|
+
command: `widget:${CREATE_USER_QUERY_WIZARD_NAME}.submit`,
|
|
1129
|
+
icon: 'fa-regular fa-check',
|
|
1130
|
+
color: 'success',
|
|
1131
|
+
hidden: '{{!api.getStatus().isLast}}',
|
|
1132
|
+
predicateApiWidgetName: CREATE_USER_QUERY_WIZARD_NAME,
|
|
1133
|
+
});
|
|
1134
|
+
})
|
|
1135
|
+
.onAction(async (dialogRef) => {
|
|
1136
|
+
if (dialogRef.action() === 'cancel') {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
return dialogRef.context() ?? null;
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
//#endregion
|
|
1143
|
+
//#region ----- Step bodies -----
|
|
1144
|
+
function fillSourceStep(layout, args) {
|
|
1145
|
+
switch (args.sourceKind) {
|
|
1146
|
+
case 'static':
|
|
1147
|
+
fillStaticSourceStep(layout);
|
|
1148
|
+
return;
|
|
1149
|
+
case 'api':
|
|
1150
|
+
fillApiSourceStep(layout);
|
|
1151
|
+
return;
|
|
1152
|
+
default:
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
function fillEntityPickStep(layout, readonly = false) {
|
|
1157
|
+
layout.fieldset((fieldset) => {
|
|
1158
|
+
fieldset.setTitle(CREATE_USER_QUERY_I18N.steps.entity).setIcon('fa-light fa-database').setCols(1);
|
|
1159
|
+
if (!readonly) {
|
|
1160
|
+
fieldset.setDescription(CREATE_USER_QUERY_I18N.placeholders.fieldsNext);
|
|
1161
|
+
}
|
|
1162
|
+
fieldset.formField(CREATE_USER_QUERY_I18N.fields.entity, (field) => {
|
|
1163
|
+
field.path('source.entity.entityName').lookupBox({
|
|
1164
|
+
entity: CREATE_USER_QUERY_ENTITY_CATALOG,
|
|
1165
|
+
valueField: 'id',
|
|
1166
|
+
textField: 'id',
|
|
1167
|
+
columns: ['name', 'module_id'],
|
|
1168
|
+
look: 'select',
|
|
1169
|
+
searchable: true,
|
|
1170
|
+
allowClear: !readonly,
|
|
1171
|
+
validations: readonly ? undefined : [{ rule: 'required' }],
|
|
1172
|
+
});
|
|
1173
|
+
if (readonly) {
|
|
1174
|
+
field.readonly(true);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
function fillEntityFieldsStep(layout, entityName, groups) {
|
|
1180
|
+
layout.flex((flex) => {
|
|
1181
|
+
flex.setDirection('column').setGap('16px').setAlignItems('stretch');
|
|
1182
|
+
flex.fieldset((fieldset) => {
|
|
1183
|
+
fieldset
|
|
1184
|
+
.setTitle(entityName)
|
|
1185
|
+
.setDescription(CREATE_USER_QUERY_I18N.placeholders.selectedEntityHint)
|
|
1186
|
+
.setIcon('fa-light fa-database')
|
|
1187
|
+
.setLook('group');
|
|
1188
|
+
});
|
|
1189
|
+
for (const group of groups) {
|
|
1190
|
+
renderFieldGroup(flex, group);
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
}
|
|
1194
|
+
function renderFieldGroup(layout, group, fieldGroupsPath = 'source.entity.fieldGroups') {
|
|
1195
|
+
if (group.kind === 'relation' && (group.children?.length ?? 0) > 0) {
|
|
1196
|
+
layout.panel((panel) => {
|
|
1197
|
+
panel.setCaption(group.title).setIcon('fa-light fa-link').setLook('outline');
|
|
1198
|
+
if (group.items.length) {
|
|
1199
|
+
panel.fieldset((fieldset) => {
|
|
1200
|
+
appendFieldGroupList(fieldset, group, CREATE_USER_QUERY_I18N.fields.hostFields, fieldGroupsPath);
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
for (const child of group.children ?? []) {
|
|
1204
|
+
renderFieldGroupOnPanel(panel, child, fieldGroupsPath);
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
layout.fieldset((fieldset) => {
|
|
1210
|
+
appendFieldGroupList(fieldset, group, group.title, fieldGroupsPath);
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
function renderFieldGroupOnPanel(panel, group, fieldGroupsPath = 'source.entity.fieldGroups') {
|
|
1214
|
+
if ((group.children?.length ?? 0) > 0) {
|
|
1215
|
+
panel.panel((inner) => {
|
|
1216
|
+
inner.setCaption(group.title).setIcon('fa-light fa-link').setLook('outline');
|
|
1217
|
+
if (group.items.length) {
|
|
1218
|
+
inner.fieldset((fieldset) => {
|
|
1219
|
+
appendFieldGroupList(fieldset, group, CREATE_USER_QUERY_I18N.fields.hostFields, fieldGroupsPath);
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
for (const child of group.children ?? []) {
|
|
1223
|
+
renderFieldGroupOnPanel(inner, child, fieldGroupsPath);
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
return;
|
|
1227
|
+
}
|
|
1228
|
+
panel.fieldset((fieldset) => {
|
|
1229
|
+
appendFieldGroupList(fieldset, group, group.title, fieldGroupsPath);
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
function appendFieldGroupList(fieldset, group, title, fieldGroupsPath = 'source.entity.fieldGroups') {
|
|
1233
|
+
fieldset.setTitle(title).setIcon(fieldGroupIcon(group.kind)).setCols(1);
|
|
1234
|
+
if (group.kind === 'host') {
|
|
1235
|
+
fieldset.setDescription(fieldGroupsPath.startsWith('source.api') ? CREATE_USER_QUERY_I18N.placeholders.apiFields : CREATE_USER_QUERY_I18N.placeholders.entityFields);
|
|
1236
|
+
}
|
|
1237
|
+
if (!group.items.length) {
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
fieldset.formField(' ', (field) => {
|
|
1241
|
+
field.path(`${fieldGroupsPath}.${group.id}`).selectionList({
|
|
1242
|
+
dataSource: group.items,
|
|
1243
|
+
valueField: 'path',
|
|
1244
|
+
textField: 'text',
|
|
1245
|
+
multiple: true,
|
|
1246
|
+
searchable: group.items.length > 8,
|
|
1247
|
+
direction: { id: 'vertical' },
|
|
1248
|
+
});
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
function fieldGroupIcon(kind) {
|
|
1252
|
+
if (kind === 'relation') {
|
|
1253
|
+
return 'fa-light fa-link';
|
|
1254
|
+
}
|
|
1255
|
+
if (kind === 'lookup') {
|
|
1256
|
+
return 'fa-light fa-diagram-nested';
|
|
1257
|
+
}
|
|
1258
|
+
return 'fa-light fa-list';
|
|
1259
|
+
}
|
|
1260
|
+
function fillHintStep(layout, titleKey, descriptionKey) {
|
|
1261
|
+
layout.fieldset((fieldset) => {
|
|
1262
|
+
fieldset.setTitle(titleKey).setDescription(descriptionKey).setIcon('fa-light fa-diagram-project').setLook('group');
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
function fillStaticSourceStep(layout) {
|
|
1266
|
+
layout.fieldset((fieldset) => {
|
|
1267
|
+
fieldset
|
|
1268
|
+
.setTitle(CREATE_USER_QUERY_I18N.steps.source)
|
|
1269
|
+
.setDescription(CREATE_USER_QUERY_I18N.placeholders.itemsHint)
|
|
1270
|
+
.setIcon('fa-light fa-list')
|
|
1271
|
+
.setCols(1)
|
|
1272
|
+
.formField(' ', (field) => {
|
|
1273
|
+
field
|
|
1274
|
+
.path('source.static.items')
|
|
1275
|
+
.setShowLabel(false)
|
|
1276
|
+
.customWidget(AXPWidgetsList.Layouts.RepeaterLayout, {
|
|
1277
|
+
hasControls: true,
|
|
1278
|
+
allowReorder: true,
|
|
1279
|
+
children: staticItemRepeaterChildren(),
|
|
1280
|
+
})
|
|
1281
|
+
.layout(1);
|
|
1282
|
+
});
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
function staticItemRepeaterChildren() {
|
|
1286
|
+
return [
|
|
1287
|
+
{
|
|
1288
|
+
type: AXPWidgetsList.Layouts.FlexLayout,
|
|
1289
|
+
mode: 'edit',
|
|
1290
|
+
options: {
|
|
1291
|
+
flexDirection: 'row',
|
|
1292
|
+
gap: '12px',
|
|
1293
|
+
alignItems: 'stretch',
|
|
1294
|
+
},
|
|
1295
|
+
children: [
|
|
1296
|
+
staticItemRepeaterField(CREATE_USER_QUERY_I18N.fields.value, 'value'),
|
|
1297
|
+
staticItemRepeaterField(CREATE_USER_QUERY_I18N.fields.title, 'title'),
|
|
1298
|
+
],
|
|
1299
|
+
},
|
|
1300
|
+
];
|
|
1301
|
+
}
|
|
1302
|
+
function staticItemRepeaterField(label, path) {
|
|
1303
|
+
return {
|
|
1304
|
+
type: AXPWidgetsList.Layouts.FlexItemLayout,
|
|
1305
|
+
options: { grow: 1 },
|
|
1306
|
+
children: [
|
|
1307
|
+
{
|
|
1308
|
+
type: AXPWidgetsList.Layouts.FormField,
|
|
1309
|
+
options: { label, showLabel: true },
|
|
1310
|
+
children: [
|
|
1311
|
+
{
|
|
1312
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
1313
|
+
path,
|
|
1314
|
+
name: path,
|
|
1315
|
+
mode: 'edit',
|
|
1316
|
+
},
|
|
1317
|
+
],
|
|
1318
|
+
},
|
|
1319
|
+
],
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
function fillApiSourceStep(layout) {
|
|
1323
|
+
layout.fieldset((fieldset) => {
|
|
1324
|
+
fieldset
|
|
1325
|
+
.setTitle(CREATE_USER_QUERY_I18N.steps.source)
|
|
1326
|
+
.setDescription(CREATE_USER_QUERY_I18N.placeholders.apiFieldsNext)
|
|
1327
|
+
.setIcon('fa-light fa-cloud-arrow-up')
|
|
1328
|
+
.setCols(1)
|
|
1329
|
+
.formField(CREATE_USER_QUERY_I18N.fields.connection, (field) => {
|
|
1330
|
+
field
|
|
1331
|
+
.path('source.api.connectionId')
|
|
1332
|
+
.lookupBox({
|
|
1333
|
+
entity: 'IntegrationManagement.IntegrationConnection',
|
|
1334
|
+
searchable: true,
|
|
1335
|
+
allowClear: true,
|
|
1336
|
+
})
|
|
1337
|
+
.layout(1);
|
|
1338
|
+
})
|
|
1339
|
+
.formField(CREATE_USER_QUERY_I18N.fields.request, (field) => {
|
|
1340
|
+
field.path('source.api.request').customWidget(REST_API_GENERATOR_WIDGET_NAME, {}).layout(1);
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
function fillContractPlaceholderStep(layout) {
|
|
1345
|
+
layout.fieldset((fieldset) => {
|
|
1346
|
+
fieldset
|
|
1347
|
+
.setTitle(CREATE_USER_QUERY_I18N.steps.contract)
|
|
1348
|
+
.setDescription(CREATE_USER_QUERY_I18N.placeholders.contract)
|
|
1349
|
+
.setIcon('fa-light fa-file-contract')
|
|
1350
|
+
.setLook('group');
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
//#endregion
|
|
1354
|
+
//#region ----- Guards & helpers -----
|
|
1355
|
+
async function canLeaveSourceStep(payload, args) {
|
|
1356
|
+
if (payload.step?.id !== 'source') {
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
const context = (payload.context ?? {});
|
|
1360
|
+
const errorKey = sourceStepErrorKey(args.sourceKind, context);
|
|
1361
|
+
if (!errorKey) {
|
|
1362
|
+
return true;
|
|
1363
|
+
}
|
|
1364
|
+
await showWizardWarning(args, errorKey);
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
async function canLeaveEntityFieldsStep(payload, args, groups) {
|
|
1368
|
+
if (payload.step?.id !== 'fields') {
|
|
1369
|
+
return true;
|
|
1370
|
+
}
|
|
1371
|
+
if (countFieldGroupItems(groups) === 0) {
|
|
1372
|
+
return true;
|
|
1373
|
+
}
|
|
1374
|
+
const context = (payload.context ?? {});
|
|
1375
|
+
const selected = collectSelectedFieldGroupPaths(context.source?.entity?.fieldGroups, groups).filter((path) => path !== 'id');
|
|
1376
|
+
if (selected.length > 0) {
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
await showWizardWarning(args, CREATE_USER_QUERY_I18N.errors.fieldsRequired);
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
function sourceStepErrorKey(sourceKind, context) {
|
|
1383
|
+
if (sourceKind === 'static') {
|
|
1384
|
+
const hasValue = (context.source?.static?.items ?? []).some((row) => String(row?.value ?? '').trim());
|
|
1385
|
+
return hasValue ? null : CREATE_USER_QUERY_I18N.errors.staticRequired;
|
|
1386
|
+
}
|
|
1387
|
+
if (sourceKind === 'api') {
|
|
1388
|
+
const url = context.source?.api?.request?.url?.trim() ?? '';
|
|
1389
|
+
return url ? null : CREATE_USER_QUERY_I18N.errors.apiUrlRequired;
|
|
1390
|
+
}
|
|
1391
|
+
return null;
|
|
1392
|
+
}
|
|
1393
|
+
function createInitialContext(sourceKind, entityName = '', fields = [], fieldGroups = {}) {
|
|
1394
|
+
const items = [{ value: '', title: '' }];
|
|
1395
|
+
return {
|
|
1396
|
+
sourceKind,
|
|
1397
|
+
source: {
|
|
1398
|
+
kind: sourceKind,
|
|
1399
|
+
entity: { entityName, fields, fieldGroups },
|
|
1400
|
+
static: { items },
|
|
1401
|
+
api: { request: createEmptyRestApiOutput() },
|
|
1402
|
+
},
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
function countFieldGroupItems(groups) {
|
|
1406
|
+
let count = 0;
|
|
1407
|
+
const walk = (group) => {
|
|
1408
|
+
count += group.items.length;
|
|
1409
|
+
for (const child of group.children ?? []) {
|
|
1410
|
+
walk(child);
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
for (const group of groups) {
|
|
1414
|
+
walk(group);
|
|
1415
|
+
}
|
|
1416
|
+
return count;
|
|
1417
|
+
}
|
|
1418
|
+
async function toFieldGroupViews(groups, translationService) {
|
|
1419
|
+
const hostTitle = resolveLabel(translationService, CREATE_USER_QUERY_I18N.fields.hostFields, 'Fields');
|
|
1420
|
+
const mapGroup = async (group) => {
|
|
1421
|
+
const resolvedName = resolveLabel(translationService, group.title, group.fallback);
|
|
1422
|
+
let title = resolvedName;
|
|
1423
|
+
if (group.kind === 'host') {
|
|
1424
|
+
title = hostTitle;
|
|
1425
|
+
}
|
|
1426
|
+
else if (group.kind === 'relation') {
|
|
1427
|
+
title = await formatRelatedGroupTitle(translationService, resolvedName);
|
|
1428
|
+
}
|
|
1429
|
+
const children = group.children?.length
|
|
1430
|
+
? await Promise.all(group.children.map((child) => mapGroup(child)))
|
|
1431
|
+
: undefined;
|
|
1432
|
+
return {
|
|
1433
|
+
id: group.id,
|
|
1434
|
+
kind: group.kind,
|
|
1435
|
+
title,
|
|
1436
|
+
items: group.items.map((item) => ({
|
|
1437
|
+
path: item.path,
|
|
1438
|
+
text: resolveLabel(translationService, item.title, item.fallback),
|
|
1439
|
+
})),
|
|
1440
|
+
children,
|
|
1441
|
+
};
|
|
1442
|
+
};
|
|
1443
|
+
return Promise.all(groups.map((group) => mapGroup(group)));
|
|
1444
|
+
}
|
|
1445
|
+
async function formatRelatedGroupTitle(translationService, name) {
|
|
1446
|
+
const translated = await translationService.translateAsync(CREATE_USER_QUERY_I18N.placeholders.relatedTo, { params: { name } });
|
|
1447
|
+
return typeof translated === 'string' && translated.trim() ? translated.trim() : name;
|
|
1448
|
+
}
|
|
1449
|
+
function resolveLabel(translationService, value, fallback) {
|
|
1450
|
+
if (value == null || value === '') {
|
|
1451
|
+
return fallback;
|
|
1452
|
+
}
|
|
1453
|
+
const resolved = translationService.resolve(value);
|
|
1454
|
+
const text = typeof resolved === 'string' ? resolved.trim() : '';
|
|
1455
|
+
return text || fallback;
|
|
1456
|
+
}
|
|
1457
|
+
async function showWizardWarning(args, errorKey, params, color = 'warning') {
|
|
1458
|
+
const message = await args.translationService.translateAsync(errorKey, params ? { params } : undefined);
|
|
1459
|
+
args.toastService.show({
|
|
1460
|
+
color,
|
|
1461
|
+
content: message,
|
|
1462
|
+
closeButton: true,
|
|
1463
|
+
timeOut: 4000,
|
|
1464
|
+
timeOutProgress: true,
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
function dialogTitleKey(sourceKind) {
|
|
1468
|
+
if (sourceKind === 'static') {
|
|
1469
|
+
return CREATE_USER_QUERY_I18N.titles.static;
|
|
1470
|
+
}
|
|
1471
|
+
if (sourceKind === 'api') {
|
|
1472
|
+
return CREATE_USER_QUERY_I18N.titles.api;
|
|
1473
|
+
}
|
|
1474
|
+
return CREATE_USER_QUERY_I18N.titles.entity;
|
|
1475
|
+
}
|
|
1476
|
+
function sourceStepIcon(sourceKind) {
|
|
1477
|
+
if (sourceKind === 'static') {
|
|
1478
|
+
return 'fa-light fa-list';
|
|
1479
|
+
}
|
|
1480
|
+
if (sourceKind === 'api') {
|
|
1481
|
+
return 'fa-light fa-cloud-arrow-up';
|
|
1482
|
+
}
|
|
1483
|
+
return 'fa-light fa-database';
|
|
1484
|
+
}
|
|
1485
|
+
//#endregion
|
|
1486
|
+
|
|
1487
|
+
//#region ----- Imports -----
|
|
1488
|
+
//#endregion
|
|
1489
|
+
//#region ----- Command -----
|
|
1490
|
+
/**
|
|
1491
|
+
* Opens the user-query create wizard. Step 1 configures the source; nothing is persisted.
|
|
1492
|
+
*/
|
|
1493
|
+
class AXMCreateUserQueryCommand {
|
|
1494
|
+
constructor() {
|
|
1495
|
+
//#region ----- Services & Dependencies -----
|
|
1496
|
+
this.layoutBuilder = inject(AXPLayoutBuilderService);
|
|
1497
|
+
this.entityResolver = inject(AXPEntityResolver);
|
|
1498
|
+
this.toastService = inject(AXToastService);
|
|
1499
|
+
this.translationService = inject(AXTranslationService);
|
|
1500
|
+
}
|
|
1501
|
+
//#endregion
|
|
1502
|
+
//#region ----- Execution -----
|
|
1503
|
+
async execute(input = {}) {
|
|
1504
|
+
const sourceKind = resolveSourceKind(input);
|
|
1505
|
+
await openCreateUserQueryWizard({
|
|
1506
|
+
layoutBuilder: this.layoutBuilder,
|
|
1507
|
+
toastService: this.toastService,
|
|
1508
|
+
translationService: this.translationService,
|
|
1509
|
+
entityResolver: this.entityResolver,
|
|
1510
|
+
sourceKind,
|
|
1511
|
+
});
|
|
1512
|
+
return {
|
|
1513
|
+
success: true,
|
|
1514
|
+
kind: 'object',
|
|
1515
|
+
data: { sourceKind },
|
|
1516
|
+
metadata: { refresh: false },
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1520
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, providedIn: 'root' }); }
|
|
1521
|
+
}
|
|
1522
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImport: i0, type: AXMCreateUserQueryCommand, decorators: [{
|
|
1523
|
+
type: Injectable,
|
|
1524
|
+
args: [{ providedIn: 'root' }]
|
|
1525
|
+
}] });
|
|
1526
|
+
//#endregion
|
|
1527
|
+
//#region ----- Helpers -----
|
|
1528
|
+
function resolveSourceKind(input) {
|
|
1529
|
+
const resolved = resolveEntityActionCommandInput(input);
|
|
1530
|
+
const data = entityActionDataRecord(resolved.data) ?? {};
|
|
1531
|
+
const options = (resolved.options ?? {});
|
|
1532
|
+
const process = (options['process'] ?? {});
|
|
1533
|
+
const processData = (process['data'] ?? {});
|
|
1534
|
+
const raw = data['sourceKind'] ?? processData['sourceKind'];
|
|
1535
|
+
if (raw === 'static' || raw === 'api' || raw === 'advanced' || raw === 'entity') {
|
|
1536
|
+
return raw;
|
|
1537
|
+
}
|
|
1538
|
+
return 'entity';
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
export { AXMCreateUserQueryCommand };
|
|
1542
|
+
//# sourceMappingURL=acorex-platform-module-platform-management-client-create-user-query.command-2Qu7aoCp.mjs.map
|