@carthooks/arcubase-cli 0.1.20 → 0.1.22
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/bundle/arcubase-admin.mjs +1669 -163
- package/bundle/arcubase.mjs +1669 -163
- package/dist/generated/command_registry.generated.d.ts +300 -0
- package/dist/generated/command_registry.generated.d.ts.map +1 -1
- package/dist/generated/command_registry.generated.js +416 -0
- package/dist/generated/help_examples.generated.d.ts +191 -0
- package/dist/generated/help_examples.generated.d.ts.map +1 -1
- package/dist/generated/help_examples.generated.js +277 -0
- package/dist/generated/type_index.generated.d.ts +44 -1
- package/dist/generated/type_index.generated.d.ts.map +1 -1
- package/dist/generated/type_index.generated.js +46 -1
- package/dist/generated/zod_registry.generated.d.ts +32 -1
- package/dist/generated/zod_registry.generated.d.ts.map +1 -1
- package/dist/generated/zod_registry.generated.js +41 -0
- package/dist/runtime/dev_sdk_gen.d.ts +9 -0
- package/dist/runtime/dev_sdk_gen.d.ts.map +1 -0
- package/dist/runtime/dev_sdk_gen.js +319 -0
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +384 -32
- package/dist/runtime/zod_registry.d.ts.map +1 -1
- package/dist/runtime/zod_registry.js +66 -0
- package/package.json +1 -1
- package/sdk-dist/api/admin/index.ts +1 -0
- package/sdk-dist/api/admin/ingress.ts +11 -0
- package/sdk-dist/api/admin/public-link.ts +48 -0
- package/sdk-dist/api/shared-link/form.ts +42 -1
- package/sdk-dist/api/user/index.ts +0 -1
- package/sdk-dist/docs/runtime-reference/access-rule.md +19 -0
- package/sdk-dist/docs/runtime-reference/dashboard.md +15 -0
- package/sdk-dist/docs/runtime-reference/dev-sdk-gen.md +41 -0
- package/sdk-dist/docs/runtime-reference/help-examples/admin/access-rule/bulk-apply.json +105 -0
- package/sdk-dist/docs/runtime-reference/help-examples/admin/dashboard/update-layout.json +16 -0
- package/sdk-dist/docs/runtime-reference/help-examples/admin/widget/create.json +33 -0
- package/sdk-dist/docs/runtime-reference/help-examples/admin/widget/preview-data.json +42 -0
- package/sdk-dist/docs/runtime-reference/widgets.md +40 -0
- package/sdk-dist/generated/command_registry.generated.ts +416 -0
- package/sdk-dist/generated/help_examples.generated.ts +277 -0
- package/sdk-dist/generated/type_index.generated.ts +46 -1
- package/sdk-dist/generated/zod_registry.generated.ts +62 -0
- package/sdk-dist/types/app.ts +37 -1
- package/sdk-dist/types/common.ts +77 -35
- package/sdk-dist/types/index.ts +1 -1
- package/sdk-dist/types/ingress.ts +31 -0
- package/sdk-dist/types/public-link.ts +10 -0
- package/sdk-dist/types/shared-link.ts +16 -1
- package/sdk-dist/types/user-action.ts +1 -43
- package/src/generated/command_registry.generated.ts +416 -0
- package/src/generated/help_examples.generated.ts +277 -0
- package/src/generated/type_index.generated.ts +46 -1
- package/src/generated/zod_registry.generated.ts +62 -0
- package/src/runtime/dev_sdk_gen.ts +360 -0
- package/src/runtime/execute.ts +484 -52
- package/src/runtime/zod_registry.ts +66 -0
- package/src/tests/command_registry.test.ts +21 -2
- package/src/tests/dev_sdk_gen.test.ts +226 -0
- package/src/tests/docs_readability.test.ts +15 -0
- package/src/tests/execute_validation.test.ts +215 -0
- package/src/tests/help.test.ts +83 -0
- package/sdk-dist/api/user/copilot.ts +0 -20
- package/sdk-dist/types/copilot.ts +0 -34
package/dist/runtime/execute.js
CHANGED
|
@@ -11,9 +11,11 @@ import { resolveArcubaseSDKPath } from './paths.js';
|
|
|
11
11
|
import { compileCSVImportMapping, compileExcelImportMapping } from './entity_import_mapping.js';
|
|
12
12
|
import { uploadLocalFileWithToken } from './local_upload.js';
|
|
13
13
|
import { renderCommandHelpExamples } from './help_examples.js';
|
|
14
|
+
import { generateArcubaseProjectSDK } from './dev_sdk_gen.js';
|
|
14
15
|
export function renderRootHelp(scope) {
|
|
15
16
|
const binary = scope === 'admin' ? 'arcubase-admin' : 'arcubase';
|
|
16
|
-
const
|
|
17
|
+
const modules = scope === 'admin' ? [...listModules(scope), 'dev'].sort() : listModules(scope);
|
|
18
|
+
const items = modules.map((item) => ` - ${item}`);
|
|
17
19
|
return [
|
|
18
20
|
`${binary} <command> [subcommand] [flags]`,
|
|
19
21
|
'',
|
|
@@ -26,6 +28,16 @@ export function renderRootHelp(scope) {
|
|
|
26
28
|
...items,
|
|
27
29
|
].join('\n');
|
|
28
30
|
}
|
|
31
|
+
function renderDevModuleHelp() {
|
|
32
|
+
return [
|
|
33
|
+
'arcubase-admin dev <command> [flags]',
|
|
34
|
+
'',
|
|
35
|
+
'developer commands for local project tooling',
|
|
36
|
+
'',
|
|
37
|
+
'commands:',
|
|
38
|
+
' - sdk-gen',
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
29
41
|
export function renderModuleHelp(scope, moduleName, env = process.env) {
|
|
30
42
|
const items = listModuleCommands(scope, moduleName);
|
|
31
43
|
if (items.length === 0) {
|
|
@@ -226,6 +238,41 @@ function getFixedValue(value) {
|
|
|
226
238
|
const fixedValue = value.fixedValue;
|
|
227
239
|
return typeof fixedValue === 'string' ? fixedValue : undefined;
|
|
228
240
|
}
|
|
241
|
+
function isDevSDKGenCommand(scope, moduleName, commandName) {
|
|
242
|
+
return scope === 'admin' && moduleName === 'dev' && commandName === 'sdk-gen';
|
|
243
|
+
}
|
|
244
|
+
function validateDevSDKGenFlags(flags) {
|
|
245
|
+
const allowed = new Set(['help', 'app-id', 'out']);
|
|
246
|
+
for (const flag of Object.keys(flags)) {
|
|
247
|
+
if (!allowed.has(flag)) {
|
|
248
|
+
throw new CLIError('UNKNOWN_FLAG', `unknown flag --${flag} for dev sdk-gen`, 2, {
|
|
249
|
+
operation: 'dev sdk-gen',
|
|
250
|
+
hint: 'run dev sdk-gen --help for the supported flags',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function renderDevSDKGenHelp() {
|
|
256
|
+
return [
|
|
257
|
+
'arcubase-admin dev sdk-gen',
|
|
258
|
+
'method: local generator',
|
|
259
|
+
'endpoint: /apps/:app_id',
|
|
260
|
+
'body: none',
|
|
261
|
+
'path flags: --app-id',
|
|
262
|
+
'flags: --out',
|
|
263
|
+
'',
|
|
264
|
+
'generates typed TypeScript source for the current Web project',
|
|
265
|
+
'',
|
|
266
|
+
'example:',
|
|
267
|
+
' - arcubase-admin dev sdk-gen --app-id <app_id> --out src/arcubase',
|
|
268
|
+
'',
|
|
269
|
+
'requirements:',
|
|
270
|
+
' - missing field.key values are initialized through admin auth as field<id>',
|
|
271
|
+
' - existing field.key values must be stable unique TypeScript identifiers',
|
|
272
|
+
' - entity property names use entity.key when available, otherwise a stable entity<id> fallback',
|
|
273
|
+
' - generated code accepts createArcubaseSdk({ baseURL, accessToken, refreshToken })',
|
|
274
|
+
].join('\n');
|
|
275
|
+
}
|
|
229
276
|
function resolveEndpoint(command, flags) {
|
|
230
277
|
let resolved = command.endpoint;
|
|
231
278
|
for (const mapping of command.pathParams) {
|
|
@@ -577,6 +624,9 @@ function buildTableCreateExampleBody() {
|
|
|
577
624
|
function isAssignUsersCommand(scope, command) {
|
|
578
625
|
return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'assign-users';
|
|
579
626
|
}
|
|
627
|
+
function isAccessRuleBulkApplyCommand(scope, command) {
|
|
628
|
+
return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'bulk-apply';
|
|
629
|
+
}
|
|
580
630
|
function isTableCreateCommand(scope, command) {
|
|
581
631
|
return scope === 'admin' && command.commandPath[0] === 'table' && command.commandPath[1] === 'create';
|
|
582
632
|
}
|
|
@@ -589,6 +639,12 @@ function isTableSchemaCommand(scope, command) {
|
|
|
589
639
|
function isAccessRuleCommand(scope, command) {
|
|
590
640
|
return scope === 'admin' && command.commandPath[0] === 'access-rule';
|
|
591
641
|
}
|
|
642
|
+
function isWidgetPreviewDataCommand(scope, command) {
|
|
643
|
+
return scope === 'admin' && command.commandPath[0] === 'widget' && command.commandPath[1] === 'preview-data';
|
|
644
|
+
}
|
|
645
|
+
function isWidgetUpdateCommand(scope, command) {
|
|
646
|
+
return scope === 'admin' && command.commandPath[0] === 'widget' && command.commandPath[1] === 'update';
|
|
647
|
+
}
|
|
592
648
|
function isAccessRuleUpdateCommand(scope, command) {
|
|
593
649
|
return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'update';
|
|
594
650
|
}
|
|
@@ -885,6 +941,45 @@ function buildBodyGuidance(requestType) {
|
|
|
885
941
|
],
|
|
886
942
|
};
|
|
887
943
|
}
|
|
944
|
+
if (requestType === 'AppPreviewWidgetsDataReqVO') {
|
|
945
|
+
return {
|
|
946
|
+
shapeHint: {
|
|
947
|
+
dataSource: 2188891001,
|
|
948
|
+
dataSourceType: 'entity',
|
|
949
|
+
dimensions: [{ IsField: true, FieldID: 1004, Name: 'Source' }],
|
|
950
|
+
measures: [{ FieldID: 1005, Aggregate: 'sum', Name: 'Total Amount' }],
|
|
951
|
+
},
|
|
952
|
+
commonMistakes: [
|
|
953
|
+
'widget preview-data body is the chart query itself',
|
|
954
|
+
'do not send a saved widget object with id, entity_id, or options',
|
|
955
|
+
'field dimensions with FieldID must set IsField:true',
|
|
956
|
+
'unsupported aggregates are invalid; use sum, avg, max, min, count, or distinct_count',
|
|
957
|
+
],
|
|
958
|
+
suggestions: [
|
|
959
|
+
'retry with: arcubase-admin widget preview-data --app-id <app_id> --body-json \'{"dataSource":<table_id>,"dataSourceType":"entity","dimensions":[{"IsField":true,"FieldID":<field_id>,"Name":"Source"}],"measures":[{"FieldID":<number_field_id>,"Aggregate":"sum","Name":"Total Amount"}]}\'',
|
|
960
|
+
],
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
if (requestType === 'AppUpdateWidgetsReqVO') {
|
|
964
|
+
return {
|
|
965
|
+
shapeHint: {
|
|
966
|
+
entity_id: 2188891001,
|
|
967
|
+
options: {
|
|
968
|
+
charts: {
|
|
969
|
+
entity_id: 2188891001,
|
|
970
|
+
dimensions: [{ IsField: true, FieldID: 1004, Name: 'Source' }],
|
|
971
|
+
measures: [{ FieldID: 1005, Aggregate: 'sum', Name: 'Total Amount' }],
|
|
972
|
+
type: 'bar',
|
|
973
|
+
},
|
|
974
|
+
},
|
|
975
|
+
},
|
|
976
|
+
commonMistakes: [
|
|
977
|
+
'dashboard chart config belongs in body.options.charts',
|
|
978
|
+
'field dimensions with FieldID must set IsField:true',
|
|
979
|
+
'verify persistence with widget get or widget list after update',
|
|
980
|
+
],
|
|
981
|
+
};
|
|
982
|
+
}
|
|
888
983
|
return {};
|
|
889
984
|
}
|
|
890
985
|
function buildBodyValidationDetails(scope, command, requestType, issues, env = process.env) {
|
|
@@ -937,31 +1032,96 @@ function validateAccessRuleUserScope(scope, command, requestType, userScope, env
|
|
|
937
1032
|
}
|
|
938
1033
|
}
|
|
939
1034
|
function normalizeNumericAccessRuleUserScopeIDs(scope, command, body) {
|
|
940
|
-
if (!isAccessRuleCommand(scope, command) || !isRecord(body)
|
|
1035
|
+
if (!isAccessRuleCommand(scope, command) || !isRecord(body)) {
|
|
941
1036
|
return body;
|
|
942
1037
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1038
|
+
const normalizeUserScope = (userScope) => {
|
|
1039
|
+
if (!Array.isArray(userScope))
|
|
1040
|
+
return { value: userScope, changed: false };
|
|
1041
|
+
let changed = false;
|
|
1042
|
+
const value = userScope.map((item) => {
|
|
1043
|
+
if (!isRecord(item) || typeof item.id !== 'string' || !/^\d+$/.test(item.id)) {
|
|
1044
|
+
return item;
|
|
1045
|
+
}
|
|
1046
|
+
const id = Number(item.id);
|
|
1047
|
+
if (!Number.isFinite(id) || !Number.isInteger(id)) {
|
|
1048
|
+
return item;
|
|
1049
|
+
}
|
|
1050
|
+
changed = true;
|
|
1051
|
+
return { ...item, id };
|
|
1052
|
+
});
|
|
1053
|
+
return { value, changed };
|
|
1054
|
+
};
|
|
1055
|
+
const normalizeNumericID = (value) => {
|
|
1056
|
+
if (typeof value !== 'string' || !/^\d+$/.test(value))
|
|
1057
|
+
return { value, changed: false };
|
|
1058
|
+
const id = Number(value);
|
|
1059
|
+
if (!Number.isFinite(id) || !Number.isInteger(id))
|
|
1060
|
+
return { value, changed: false };
|
|
1061
|
+
return { value: id, changed: true };
|
|
1062
|
+
};
|
|
1063
|
+
if (isAccessRuleBulkApplyCommand(scope, command) && Array.isArray(body.items)) {
|
|
1064
|
+
let changed = false;
|
|
1065
|
+
const items = body.items.map((item) => {
|
|
1066
|
+
if (!isRecord(item))
|
|
1067
|
+
return item;
|
|
1068
|
+
let nextItem = item;
|
|
1069
|
+
for (const key of ['table_id', 'rule_id']) {
|
|
1070
|
+
const normalized = normalizeNumericID(nextItem[key]);
|
|
1071
|
+
if (normalized.changed) {
|
|
1072
|
+
nextItem = { ...nextItem, [key]: normalized.value };
|
|
1073
|
+
changed = true;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
if (isRecord(nextItem.options)) {
|
|
1077
|
+
const normalizedUserScope = normalizeUserScope(nextItem.options.user_scope);
|
|
1078
|
+
if (normalizedUserScope.changed) {
|
|
1079
|
+
nextItem = {
|
|
1080
|
+
...nextItem,
|
|
1081
|
+
options: {
|
|
1082
|
+
...nextItem.options,
|
|
1083
|
+
user_scope: normalizedUserScope.value,
|
|
1084
|
+
},
|
|
1085
|
+
};
|
|
1086
|
+
changed = true;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
return nextItem;
|
|
1090
|
+
});
|
|
1091
|
+
return changed ? { ...body, items } : body;
|
|
1092
|
+
}
|
|
1093
|
+
if (!isRecord(body.options)) {
|
|
1094
|
+
return body;
|
|
1095
|
+
}
|
|
1096
|
+
const normalizedUserScope = normalizeUserScope(body.options.user_scope);
|
|
1097
|
+
if (!normalizedUserScope.changed)
|
|
956
1098
|
return body;
|
|
957
1099
|
return {
|
|
958
1100
|
...body,
|
|
959
1101
|
options: {
|
|
960
1102
|
...body.options,
|
|
961
|
-
user_scope:
|
|
1103
|
+
user_scope: normalizedUserScope.value,
|
|
962
1104
|
},
|
|
963
1105
|
};
|
|
964
1106
|
}
|
|
1107
|
+
function validateAccessRulePolicyShape(scope, command, requestType, policy, env, pathPrefix) {
|
|
1108
|
+
const actions = policy && Array.isArray(policy.actions) ? policy.actions : [];
|
|
1109
|
+
if (actions.includes('read') || actions.includes('write')) {
|
|
1110
|
+
throwBodyValidationFailure('access-rule policy.actions must use Arcubase action codes: use "view" for read and "add","edit" for write; do not use "read", "write", or "insert"', requestType, `${pathPrefix}.actions`, scope, command, env);
|
|
1111
|
+
}
|
|
1112
|
+
const fields = policy && Array.isArray(policy.fields) ? policy.fields : [];
|
|
1113
|
+
const allowedSystemFieldKeys = new Set(['created', 'updated', 'creator']);
|
|
1114
|
+
for (const [index, field] of fields.entries()) {
|
|
1115
|
+
if (!isRecord(field))
|
|
1116
|
+
continue;
|
|
1117
|
+
if (typeof field.key === 'number' || (typeof field.key === 'string' && /^\d+$/.test(field.key))) {
|
|
1118
|
+
throwBodyValidationFailure('access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers', requestType, `${pathPrefix}.fields.${index}.key`, scope, command, env);
|
|
1119
|
+
}
|
|
1120
|
+
if (typeof field.key === 'string' && !field.key.startsWith(':') && !allowedSystemFieldKeys.has(field.key)) {
|
|
1121
|
+
throwBodyValidationFailure('access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"', requestType, `${pathPrefix}.fields.${index}.key`, scope, command, env);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
965
1125
|
function requireUpdateContains(scope, command, body, field, env) {
|
|
966
1126
|
if (!(field in body))
|
|
967
1127
|
return;
|
|
@@ -970,6 +1130,18 @@ function requireUpdateContains(scope, command, body, field, env) {
|
|
|
970
1130
|
throwBodyValidationFailure(`body.update must include "${field}" when body.${field} is present. Example: {"update":["${field}"],"${field}":${field === 'enabled' ? 'true' : '"value"'}}`, command.requestType ?? 'AppIngressUpdateReqVO', 'body.update', scope, command, env);
|
|
971
1131
|
}
|
|
972
1132
|
}
|
|
1133
|
+
function validateChartFieldDimensions(scope, command, requestType, dimensions, pathPrefix, env) {
|
|
1134
|
+
if (!Array.isArray(dimensions))
|
|
1135
|
+
return;
|
|
1136
|
+
for (const [index, dimension] of dimensions.entries()) {
|
|
1137
|
+
if (!isRecord(dimension) || typeof dimension.FieldID !== 'number') {
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
if (dimension.IsField !== true) {
|
|
1141
|
+
throwBodyValidationFailure('widget chart field dimensions require IsField:true when FieldID is present; otherwise grouped statistics collapse into total rows', requestType, `${pathPrefix}.${index}.IsField`, scope, command, env);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
973
1145
|
function validateActionSpecificRawBody(scope, command, body, env) {
|
|
974
1146
|
if (!isRecord(body) || !command.requestType) {
|
|
975
1147
|
return;
|
|
@@ -987,6 +1159,26 @@ function validateActionSpecificRawBody(scope, command, body, env) {
|
|
|
987
1159
|
}
|
|
988
1160
|
}
|
|
989
1161
|
if (isAccessRuleCommand(scope, command)) {
|
|
1162
|
+
if (isAccessRuleBulkApplyCommand(scope, command)) {
|
|
1163
|
+
if (!Array.isArray(body.items) || body.items.length === 0) {
|
|
1164
|
+
throwBodyValidationFailure('access-rule bulk-apply requires non-empty body.items; put every table/rule assignment in body.items', command.requestType, 'body.items', scope, command, env);
|
|
1165
|
+
}
|
|
1166
|
+
if (body.mode !== undefined && body.mode !== 'update_existing' && body.mode !== 'upsert_by_name') {
|
|
1167
|
+
throwBodyValidationFailure('access-rule bulk-apply body.mode must be "update_existing" or "upsert_by_name"', command.requestType, 'body.mode', scope, command, env);
|
|
1168
|
+
}
|
|
1169
|
+
for (const [index, item] of body.items.entries()) {
|
|
1170
|
+
if (!isRecord(item)) {
|
|
1171
|
+
throwBodyValidationFailure('access-rule bulk-apply body.items[] must be an object', command.requestType, `body.items.${index}`, scope, command, env);
|
|
1172
|
+
}
|
|
1173
|
+
const options = isRecord(item.options) ? item.options : undefined;
|
|
1174
|
+
if (!options) {
|
|
1175
|
+
throwBodyValidationFailure('access-rule bulk-apply body.items[].options is required and must include the full canonical options object', command.requestType, `body.items.${index}.options`, scope, command, env);
|
|
1176
|
+
}
|
|
1177
|
+
validateAccessRuleUserScope(scope, command, command.requestType, options.user_scope, env, `body.items.${index}.options.user_scope`);
|
|
1178
|
+
validateAccessRulePolicyShape(scope, command, command.requestType, isRecord(options.policy) ? options.policy : undefined, env, `body.items.${index}.options.policy`);
|
|
1179
|
+
}
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
990
1182
|
const update = stringArray(body.update);
|
|
991
1183
|
if (isAccessRuleUpdateCommand(scope, command) && update.some((item) => item.startsWith('options.'))) {
|
|
992
1184
|
throwBodyValidationFailure('access-rule update cannot patch nested options paths. Use {"update":["options"],"options":{"policy":{"key":"custom","actions":["view","add","edit"],"all_fields_read":true,"all_fields_write":true,"fields":[{"key":":1002","read":false,"write":false,"report":false}]},"list_options":{}}}', command.requestType, 'body.update', scope, command, env);
|
|
@@ -1003,22 +1195,15 @@ function validateActionSpecificRawBody(scope, command, body, env) {
|
|
|
1003
1195
|
throwBodyValidationFailure('access-rule update with update:["options"] cannot change only options.user_scope because it overwrites policy. Use access-rule assign-users for user assignment, or include the full options.policy and options.list_options.', command.requestType, 'body.options.policy', scope, command, env);
|
|
1004
1196
|
}
|
|
1005
1197
|
validateAccessRuleUserScope(scope, command, command.requestType, options?.user_scope, env);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
if (typeof field.key === 'number' || (typeof field.key === 'string' && /^\d+$/.test(field.key))) {
|
|
1016
|
-
throwBodyValidationFailure('access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers', command.requestType, `body.options.policy.fields.${index}.key`, scope, command, env);
|
|
1017
|
-
}
|
|
1018
|
-
if (typeof field.key === 'string' && !field.key.startsWith(':') && !allowedSystemFieldKeys.has(field.key)) {
|
|
1019
|
-
throwBodyValidationFailure('access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"', command.requestType, `body.options.policy.fields.${index}.key`, scope, command, env);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1198
|
+
validateAccessRulePolicyShape(scope, command, command.requestType, policy, env, 'body.options.policy');
|
|
1199
|
+
}
|
|
1200
|
+
if (isWidgetPreviewDataCommand(scope, command)) {
|
|
1201
|
+
validateChartFieldDimensions(scope, command, command.requestType, body.dimensions, 'body.dimensions', env);
|
|
1202
|
+
}
|
|
1203
|
+
if (isWidgetUpdateCommand(scope, command)) {
|
|
1204
|
+
const options = isRecord(body.options) ? body.options : undefined;
|
|
1205
|
+
const charts = options && isRecord(options.charts) ? options.charts : undefined;
|
|
1206
|
+
validateChartFieldDimensions(scope, command, command.requestType, charts?.dimensions, 'body.options.charts.dimensions', env);
|
|
1022
1207
|
}
|
|
1023
1208
|
}
|
|
1024
1209
|
function validateActionSpecificBody(scope, command, flags, body, env) {
|
|
@@ -1370,6 +1555,163 @@ async function requestJSON(runtimeEnv, method, endpoint, body, fetchImpl) {
|
|
|
1370
1555
|
}
|
|
1371
1556
|
return { status: response.status, data: parsedResponse };
|
|
1372
1557
|
}
|
|
1558
|
+
function unwrapResponseData(payload) {
|
|
1559
|
+
return isRecord(payload) && 'data' in payload ? payload.data : payload;
|
|
1560
|
+
}
|
|
1561
|
+
function extractSDKGenAppPayload(payload) {
|
|
1562
|
+
const appPayload = unwrapResponseData(payload);
|
|
1563
|
+
if (!isRecord(appPayload)) {
|
|
1564
|
+
throw new CLIError('SDK_GEN_INVALID_SCHEMA', 'sdk-gen expected app detail response data', 2);
|
|
1565
|
+
}
|
|
1566
|
+
return appPayload;
|
|
1567
|
+
}
|
|
1568
|
+
function extractSDKGenEntityRefs(appPayload) {
|
|
1569
|
+
const sourceEntities = Array.isArray(appPayload.entities)
|
|
1570
|
+
? appPayload.entities
|
|
1571
|
+
: Array.isArray(appPayload.tables)
|
|
1572
|
+
? appPayload.tables
|
|
1573
|
+
: Array.isArray(appPayload.entitys)
|
|
1574
|
+
? appPayload.entitys
|
|
1575
|
+
: [];
|
|
1576
|
+
const refs = sourceEntities
|
|
1577
|
+
.filter((item) => isRecord(item))
|
|
1578
|
+
.map((item) => item.id)
|
|
1579
|
+
.filter((id) => typeof id === 'string' || typeof id === 'number')
|
|
1580
|
+
.map((id) => ({ id: String(id) }));
|
|
1581
|
+
return refs;
|
|
1582
|
+
}
|
|
1583
|
+
function resolveSDKGenAppId(flagAppId, appPayload) {
|
|
1584
|
+
const fromPayload = typeof appPayload.id === 'number' ? appPayload.id : typeof appPayload.app_id === 'number' ? appPayload.app_id : undefined;
|
|
1585
|
+
const fromFlag = Number(flagAppId);
|
|
1586
|
+
if (Number.isFinite(fromFlag))
|
|
1587
|
+
return fromFlag;
|
|
1588
|
+
if (fromPayload)
|
|
1589
|
+
return fromPayload;
|
|
1590
|
+
throw new CLIError('SDK_GEN_APP_ID_REQUIRED', 'sdk-gen expected numeric app id', 2);
|
|
1591
|
+
}
|
|
1592
|
+
function walkSDKGenFields(fields, visit) {
|
|
1593
|
+
if (!Array.isArray(fields)) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
for (const field of fields) {
|
|
1597
|
+
if (!isRecord(field)) {
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
visit(field);
|
|
1601
|
+
walkSDKGenFields(field.children, visit);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
function collectExistingSDKGenFieldKeys(entity) {
|
|
1605
|
+
const keys = new Set();
|
|
1606
|
+
walkSDKGenFields(entity.fields, (field) => {
|
|
1607
|
+
if (typeof field.key === 'string' && field.key.trim() !== '') {
|
|
1608
|
+
keys.add(field.key.trim());
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
return keys;
|
|
1612
|
+
}
|
|
1613
|
+
function nextSDKGenFallbackFieldKey(fieldId, usedKeys) {
|
|
1614
|
+
const base = `field${fieldId}`;
|
|
1615
|
+
if (!usedKeys.has(base)) {
|
|
1616
|
+
usedKeys.add(base);
|
|
1617
|
+
return base;
|
|
1618
|
+
}
|
|
1619
|
+
let index = 2;
|
|
1620
|
+
while (usedKeys.has(`${base}_${index}`)) {
|
|
1621
|
+
index++;
|
|
1622
|
+
}
|
|
1623
|
+
const key = `${base}_${index}`;
|
|
1624
|
+
usedKeys.add(key);
|
|
1625
|
+
return key;
|
|
1626
|
+
}
|
|
1627
|
+
function initializeMissingSDKGenFieldKeys(entity) {
|
|
1628
|
+
const usedKeys = collectExistingSDKGenFieldKeys(entity);
|
|
1629
|
+
const updates = [];
|
|
1630
|
+
walkSDKGenFields(entity.fields, (field) => {
|
|
1631
|
+
if (typeof field.id !== 'number') {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (typeof field.key === 'string' && field.key.trim() !== '') {
|
|
1635
|
+
field.key = field.key.trim();
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const key = nextSDKGenFallbackFieldKey(field.id, usedKeys);
|
|
1639
|
+
field.key = key;
|
|
1640
|
+
updates.push({ id: field.id, key });
|
|
1641
|
+
});
|
|
1642
|
+
return updates;
|
|
1643
|
+
}
|
|
1644
|
+
async function executeDevSDKGen(runtimeEnv, flags, fetchImpl) {
|
|
1645
|
+
validateDevSDKGenFlags(flags);
|
|
1646
|
+
const appId = flagValue(flags, 'app-id');
|
|
1647
|
+
const outDir = flagValue(flags, 'out');
|
|
1648
|
+
if (!appId) {
|
|
1649
|
+
throw new CLIError('MISSING_PATH_FLAG', 'missing required flag --app-id', 2);
|
|
1650
|
+
}
|
|
1651
|
+
if (!outDir) {
|
|
1652
|
+
throw new CLIError('MISSING_FLAG', 'missing required flag --out', 2, {
|
|
1653
|
+
operation: 'dev sdk-gen',
|
|
1654
|
+
hint: 'retry with --out src/arcubase',
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
const endpoint = `/api/apps/${encodeURIComponent(appId)}`;
|
|
1658
|
+
const appDetail = await requestJSON(runtimeEnv, 'GET', endpoint, undefined, fetchImpl);
|
|
1659
|
+
const appPayload = extractSDKGenAppPayload(appDetail.data);
|
|
1660
|
+
let entityRefs = extractSDKGenEntityRefs(appPayload);
|
|
1661
|
+
if (entityRefs.length === 0) {
|
|
1662
|
+
const entitiesEndpoint = `/api/apps/${encodeURIComponent(appId)}/entities`;
|
|
1663
|
+
const entityList = await requestJSON(runtimeEnv, 'GET', entitiesEndpoint, undefined, fetchImpl);
|
|
1664
|
+
const entityListPayload = unwrapResponseData(entityList.data);
|
|
1665
|
+
entityRefs = extractSDKGenEntityRefs({ entities: entityListPayload });
|
|
1666
|
+
}
|
|
1667
|
+
if (entityRefs.length === 0) {
|
|
1668
|
+
throw new CLIError('SDK_GEN_NO_ENTITIES', 'sdk-gen could not find entities in app detail response', 2);
|
|
1669
|
+
}
|
|
1670
|
+
const entities = [];
|
|
1671
|
+
const initializedFieldKeys = [];
|
|
1672
|
+
for (const entityRef of entityRefs) {
|
|
1673
|
+
const entityEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}`;
|
|
1674
|
+
const entityDetail = await requestJSON(runtimeEnv, 'GET', entityEndpoint, undefined, fetchImpl);
|
|
1675
|
+
const entityPayload = unwrapResponseData(entityDetail.data);
|
|
1676
|
+
if (!isRecord(entityPayload)) {
|
|
1677
|
+
throw new CLIError('SDK_GEN_INVALID_SCHEMA', 'sdk-gen expected entity detail response data', 2);
|
|
1678
|
+
}
|
|
1679
|
+
const customKeyUpdates = initializeMissingSDKGenFieldKeys(entityPayload);
|
|
1680
|
+
if (customKeyUpdates.length > 0) {
|
|
1681
|
+
const customKeysEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}/custom-keys`;
|
|
1682
|
+
await requestJSON(runtimeEnv, 'PUT', customKeysEndpoint, customKeyUpdates, fetchImpl);
|
|
1683
|
+
initializedFieldKeys.push({
|
|
1684
|
+
entityId: typeof entityPayload.id === 'number' ? entityPayload.id : Number(entityRef.id),
|
|
1685
|
+
fields: customKeyUpdates.map((item) => item.key),
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
entities.push(entityPayload);
|
|
1689
|
+
}
|
|
1690
|
+
const generated = generateArcubaseProjectSDK({
|
|
1691
|
+
id: resolveSDKGenAppId(appId, appPayload),
|
|
1692
|
+
name: typeof appPayload.name === 'string' ? appPayload.name : undefined,
|
|
1693
|
+
entities,
|
|
1694
|
+
});
|
|
1695
|
+
const absoluteOutDir = path.resolve(process.cwd(), outDir);
|
|
1696
|
+
for (const file of generated.files) {
|
|
1697
|
+
const targetPath = path.resolve(absoluteOutDir, file.path);
|
|
1698
|
+
if (!targetPath.startsWith(`${absoluteOutDir}${path.sep}`)) {
|
|
1699
|
+
throw new CLIError('SDK_GEN_INVALID_OUTPUT_PATH', `invalid generated file path: ${file.path}`, 2);
|
|
1700
|
+
}
|
|
1701
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
1702
|
+
fs.writeFileSync(targetPath, file.contents);
|
|
1703
|
+
}
|
|
1704
|
+
return {
|
|
1705
|
+
kind: 'result',
|
|
1706
|
+
commandPath: ['dev', 'sdk-gen'],
|
|
1707
|
+
status: appDetail.status,
|
|
1708
|
+
data: {
|
|
1709
|
+
out: absoluteOutDir,
|
|
1710
|
+
files: generated.files.map((file) => file.path),
|
|
1711
|
+
initializedFieldKeys,
|
|
1712
|
+
},
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1373
1715
|
function requiredStringFlag(flags, flag) {
|
|
1374
1716
|
const value = flagValue(flags, flag);
|
|
1375
1717
|
if (!value) {
|
|
@@ -1699,6 +2041,16 @@ export async function executeCLI(scope, argv, env, fetchImpl = fetch) {
|
|
|
1699
2041
|
}
|
|
1700
2042
|
return { kind: 'help', text: renderRootHelp(scope) };
|
|
1701
2043
|
}
|
|
2044
|
+
if (isDevSDKGenCommand(scope, moduleName, commandName)) {
|
|
2045
|
+
if (hasFlag(parsed.flags, 'help')) {
|
|
2046
|
+
return { kind: 'help', text: renderDevSDKGenHelp() };
|
|
2047
|
+
}
|
|
2048
|
+
const runtimeEnv = env ?? loadRuntimeEnv(scope);
|
|
2049
|
+
return executeDevSDKGen(runtimeEnv, parsed.flags, fetchImpl);
|
|
2050
|
+
}
|
|
2051
|
+
if (scope === 'admin' && moduleName === 'dev' && !commandName) {
|
|
2052
|
+
return { kind: 'help', text: renderDevModuleHelp() };
|
|
2053
|
+
}
|
|
1702
2054
|
const singleTokenCommand = !commandName ? findCommand(scope, moduleName) : null;
|
|
1703
2055
|
if (!commandName && !singleTokenCommand) {
|
|
1704
2056
|
if (hasFlag(parsed.flags, 'help-examples')) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod_registry.d.ts","sourceRoot":"","sources":["../../src/runtime/zod_registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AA8BrC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAOjE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAQ9D;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"zod_registry.d.ts","sourceRoot":"","sources":["../../src/runtime/zod_registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AA8BrC,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAOjE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI9E;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAQ9D;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAiJlD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,eAAe,EAAE,CAqB7F;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,cAAc,EAAE,CAI3F;AAkGD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,cAAc,EAAE,CAInG"}
|
|
@@ -60,11 +60,31 @@ const conditionDocHint = {
|
|
|
60
60
|
title: 'Condition',
|
|
61
61
|
file: 'docs/runtime-reference/condition.md',
|
|
62
62
|
};
|
|
63
|
+
const dashboardDocHint = {
|
|
64
|
+
title: 'Dashboard',
|
|
65
|
+
file: 'docs/runtime-reference/dashboard.md',
|
|
66
|
+
};
|
|
67
|
+
const widgetsDocHint = {
|
|
68
|
+
title: 'Widgets',
|
|
69
|
+
file: 'docs/runtime-reference/widgets.md',
|
|
70
|
+
};
|
|
63
71
|
const docHintIndex = {
|
|
64
72
|
AppCreateByTenantsReqVO: [
|
|
65
73
|
{ title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
|
|
66
74
|
examplesIndexDocHint,
|
|
67
75
|
],
|
|
76
|
+
AppNewWidgetsReqVO: [
|
|
77
|
+
widgetsDocHint,
|
|
78
|
+
],
|
|
79
|
+
AppPreviewWidgetsDataReqVO: [
|
|
80
|
+
widgetsDocHint,
|
|
81
|
+
],
|
|
82
|
+
AppUpdateWidgetsIngressOptionsReqVO: [
|
|
83
|
+
widgetsDocHint,
|
|
84
|
+
],
|
|
85
|
+
AppUpdateWidgetsReqVO: [
|
|
86
|
+
widgetsDocHint,
|
|
87
|
+
],
|
|
68
88
|
AppCreateEntityReqVO: [
|
|
69
89
|
{ title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
|
|
70
90
|
{ title: 'Table schema cheatsheet', file: 'docs/runtime-reference/entity-schema.md' },
|
|
@@ -79,6 +99,23 @@ const docHintIndex = {
|
|
|
79
99
|
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
80
100
|
examplesIndexDocHint,
|
|
81
101
|
],
|
|
102
|
+
AppIngressBulkApplyReqVO: [
|
|
103
|
+
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
104
|
+
examplesIndexDocHint,
|
|
105
|
+
],
|
|
106
|
+
DashboardCreateReqVO: [
|
|
107
|
+
dashboardDocHint,
|
|
108
|
+
],
|
|
109
|
+
DashboardUpdateLayoutReqVO: [
|
|
110
|
+
dashboardDocHint,
|
|
111
|
+
widgetsDocHint,
|
|
112
|
+
],
|
|
113
|
+
DashboardUpdateOptionsReqVO: [
|
|
114
|
+
dashboardDocHint,
|
|
115
|
+
],
|
|
116
|
+
DashboardUpdateTitleReqVO: [
|
|
117
|
+
dashboardDocHint,
|
|
118
|
+
],
|
|
82
119
|
EntitySaveReqVO: [
|
|
83
120
|
{ title: 'Table schema cheatsheet', file: 'docs/runtime-reference/entity-schema.md' },
|
|
84
121
|
{ title: 'Field type index', file: 'docs/runtime-reference/entity-schema/README.md' },
|
|
@@ -118,6 +155,9 @@ const docHintIndex = {
|
|
|
118
155
|
{ title: 'Search and bulk actions', file: 'docs/runtime-reference/search-and-bulk-actions.md' },
|
|
119
156
|
examplesIndexDocHint,
|
|
120
157
|
],
|
|
158
|
+
FetchWidgetsDataReqVO: [
|
|
159
|
+
widgetsDocHint,
|
|
160
|
+
],
|
|
121
161
|
'EntityInvokeBatchOperatorReqVO & InvokeRequestVO': [
|
|
122
162
|
selectionDocHint,
|
|
123
163
|
conditionDocHint,
|
|
@@ -174,6 +214,21 @@ const commandDocHintIndex = {
|
|
|
174
214
|
{ title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
|
|
175
215
|
examplesIndexDocHint,
|
|
176
216
|
],
|
|
217
|
+
'admin.dashboard.getDashboardList': [
|
|
218
|
+
dashboardDocHint,
|
|
219
|
+
],
|
|
220
|
+
'admin.dashboard.getDashboardOptions': [
|
|
221
|
+
dashboardDocHint,
|
|
222
|
+
],
|
|
223
|
+
'admin.widget.widgetsList': [
|
|
224
|
+
widgetsDocHint,
|
|
225
|
+
],
|
|
226
|
+
'admin.widget.getWidgets': [
|
|
227
|
+
widgetsDocHint,
|
|
228
|
+
],
|
|
229
|
+
'admin.widget.deleteWidgets': [
|
|
230
|
+
widgetsDocHint,
|
|
231
|
+
],
|
|
177
232
|
'admin.app.createEntity': [
|
|
178
233
|
{ title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
|
|
179
234
|
examplesIndexDocHint,
|
|
@@ -197,6 +252,10 @@ const commandDocHintIndex = {
|
|
|
197
252
|
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
198
253
|
examplesIndexDocHint,
|
|
199
254
|
],
|
|
255
|
+
'admin.access-rule.bulkApplyEntityIngress': [
|
|
256
|
+
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
257
|
+
examplesIndexDocHint,
|
|
258
|
+
],
|
|
200
259
|
'user.row.insertEntity': [
|
|
201
260
|
{ title: 'Row CRUD', file: 'docs/runtime-reference/row-crud.md' },
|
|
202
261
|
{ title: 'Uploads', file: 'docs/runtime-reference/uploads.md' },
|
|
@@ -237,6 +296,13 @@ const commandDocHintIndex = {
|
|
|
237
296
|
{ title: 'Search and bulk actions', file: 'docs/runtime-reference/search-and-bulk-actions.md' },
|
|
238
297
|
examplesIndexDocHint,
|
|
239
298
|
],
|
|
299
|
+
'user.dashboard.getDashboard': [
|
|
300
|
+
dashboardDocHint,
|
|
301
|
+
],
|
|
302
|
+
'user.dashboard.getDashboardWidgets': [
|
|
303
|
+
dashboardDocHint,
|
|
304
|
+
widgetsDocHint,
|
|
305
|
+
],
|
|
240
306
|
};
|
|
241
307
|
export function getCommandDocHints(operation, env = process.env) {
|
|
242
308
|
const key = String(operation || '').trim();
|
package/package.json
CHANGED
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
AppIngressGetBlocksRespVO,
|
|
8
8
|
AppIngressGetByHashIDRespVO,
|
|
9
9
|
AppIngressListRespVO,
|
|
10
|
+
AppIngressBulkApplyReqVO,
|
|
11
|
+
AppIngressBulkApplyRespVO,
|
|
10
12
|
AppIngressUpdateReqVO,
|
|
11
13
|
AppIngressUpdateRespVO,
|
|
12
14
|
AppIngressUpdateBlocksReqVO,
|
|
@@ -83,6 +85,15 @@ export async function getEntityIngressList(
|
|
|
83
85
|
return getDefaultClient().get(`/apps/${appId}/entity/${entityId}/ingress`)
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
// @endpoint POST /apps/:app_id/access-rules/bulk-apply
|
|
89
|
+
// @controller App.IngressBulkApply
|
|
90
|
+
export async function bulkApplyEntityIngress(
|
|
91
|
+
appId: string,
|
|
92
|
+
data: AppIngressBulkApplyReqVO
|
|
93
|
+
): Promise<AppIngressBulkApplyRespVO> {
|
|
94
|
+
return getDefaultClient().post(`/apps/${appId}/access-rules/bulk-apply`, data)
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
// @endpoint PUT /apps/:app_id/entity/:entity_id/ingress/:id
|
|
87
98
|
// @controller App.IngressUpdate
|
|
88
99
|
export async function updateEntityIngress(
|