@carthooks/arcubase-cli 0.1.20 → 0.1.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/bundle/arcubase-admin.mjs +339 -51
- package/bundle/arcubase.mjs +339 -51
- package/dist/generated/command_registry.generated.d.ts +15 -0
- package/dist/generated/command_registry.generated.d.ts.map +1 -1
- package/dist/generated/command_registry.generated.js +21 -0
- package/dist/generated/help_examples.generated.d.ts +114 -0
- package/dist/generated/help_examples.generated.d.ts.map +1 -1
- package/dist/generated/help_examples.generated.js +155 -0
- package/dist/generated/type_index.generated.d.ts +4 -0
- package/dist/generated/type_index.generated.d.ts.map +1 -1
- package/dist/generated/type_index.generated.js +4 -0
- package/dist/generated/zod_registry.generated.d.ts +4 -1
- package/dist/generated/zod_registry.generated.d.ts.map +1 -1
- package/dist/generated/zod_registry.generated.js +4 -0
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +104 -31
- package/dist/runtime/zod_registry.d.ts.map +1 -1
- package/dist/runtime/zod_registry.js +8 -0
- package/package.json +1 -1
- package/sdk-dist/api/admin/ingress.ts +11 -0
- package/sdk-dist/docs/runtime-reference/access-rule.md +19 -0
- package/sdk-dist/docs/runtime-reference/help-examples/admin/access-rule/bulk-apply.json +105 -0
- package/sdk-dist/generated/command_registry.generated.ts +21 -0
- package/sdk-dist/generated/help_examples.generated.ts +155 -0
- package/sdk-dist/generated/type_index.generated.ts +4 -0
- package/sdk-dist/generated/zod_registry.generated.ts +6 -0
- package/sdk-dist/types/ingress.ts +31 -0
- package/src/generated/command_registry.generated.ts +21 -0
- package/src/generated/help_examples.generated.ts +155 -0
- package/src/generated/type_index.generated.ts +4 -0
- package/src/generated/zod_registry.generated.ts +6 -0
- package/src/runtime/execute.ts +159 -51
- package/src/runtime/zod_registry.ts +8 -0
- package/src/tests/command_registry.test.ts +1 -0
- package/src/tests/execute_validation.test.ts +86 -0
- package/src/tests/help.test.ts +13 -0
package/src/runtime/execute.ts
CHANGED
|
@@ -637,6 +637,10 @@ function isAssignUsersCommand(scope: CommandScope, command: NonNullable<ReturnTy
|
|
|
637
637
|
return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'assign-users'
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
+
function isAccessRuleBulkApplyCommand(scope: CommandScope, command: NonNullable<ReturnType<typeof findCommand>>): boolean {
|
|
641
|
+
return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'bulk-apply'
|
|
642
|
+
}
|
|
643
|
+
|
|
640
644
|
function isTableCreateCommand(scope: CommandScope, command: NonNullable<ReturnType<typeof findCommand>>): boolean {
|
|
641
645
|
return scope === 'admin' && command.commandPath[0] === 'table' && command.commandPath[1] === 'create'
|
|
642
646
|
}
|
|
@@ -1085,33 +1089,124 @@ function normalizeNumericAccessRuleUserScopeIDs(
|
|
|
1085
1089
|
command: NonNullable<ReturnType<typeof findCommand>>,
|
|
1086
1090
|
body: unknown,
|
|
1087
1091
|
): unknown {
|
|
1088
|
-
if (!isAccessRuleCommand(scope, command) || !isRecord(body)
|
|
1092
|
+
if (!isAccessRuleCommand(scope, command) || !isRecord(body)) {
|
|
1089
1093
|
return body
|
|
1090
1094
|
}
|
|
1091
1095
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1096
|
+
const normalizeUserScope = (userScope: unknown): { value: unknown; changed: boolean } => {
|
|
1097
|
+
if (!Array.isArray(userScope)) return { value: userScope, changed: false }
|
|
1098
|
+
let changed = false
|
|
1099
|
+
const value = userScope.map((item) => {
|
|
1100
|
+
if (!isRecord(item) || typeof item.id !== 'string' || !/^\d+$/.test(item.id)) {
|
|
1101
|
+
return item
|
|
1102
|
+
}
|
|
1103
|
+
const id = Number(item.id)
|
|
1104
|
+
if (!Number.isFinite(id) || !Number.isInteger(id)) {
|
|
1105
|
+
return item
|
|
1106
|
+
}
|
|
1107
|
+
changed = true
|
|
1108
|
+
return { ...item, id }
|
|
1109
|
+
})
|
|
1110
|
+
return { value, changed }
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
const normalizeNumericID = (value: unknown): { value: unknown; changed: boolean } => {
|
|
1114
|
+
if (typeof value !== 'string' || !/^\d+$/.test(value)) return { value, changed: false }
|
|
1115
|
+
const id = Number(value)
|
|
1116
|
+
if (!Number.isFinite(id) || !Number.isInteger(id)) return { value, changed: false }
|
|
1117
|
+
return { value: id, changed: true }
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
if (isAccessRuleBulkApplyCommand(scope, command) && Array.isArray(body.items)) {
|
|
1121
|
+
let changed = false
|
|
1122
|
+
const items = body.items.map((item) => {
|
|
1123
|
+
if (!isRecord(item)) return item
|
|
1124
|
+
let nextItem: Record<string, unknown> = item
|
|
1125
|
+
for (const key of ['table_id', 'rule_id']) {
|
|
1126
|
+
const normalized = normalizeNumericID(nextItem[key])
|
|
1127
|
+
if (normalized.changed) {
|
|
1128
|
+
nextItem = { ...nextItem, [key]: normalized.value }
|
|
1129
|
+
changed = true
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (isRecord(nextItem.options)) {
|
|
1133
|
+
const normalizedUserScope = normalizeUserScope(nextItem.options.user_scope)
|
|
1134
|
+
if (normalizedUserScope.changed) {
|
|
1135
|
+
nextItem = {
|
|
1136
|
+
...nextItem,
|
|
1137
|
+
options: {
|
|
1138
|
+
...nextItem.options,
|
|
1139
|
+
user_scope: normalizedUserScope.value,
|
|
1140
|
+
},
|
|
1141
|
+
}
|
|
1142
|
+
changed = true
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return nextItem
|
|
1146
|
+
})
|
|
1147
|
+
return changed ? { ...body, items } : body
|
|
1148
|
+
}
|
|
1104
1149
|
|
|
1105
|
-
if (!
|
|
1150
|
+
if (!isRecord(body.options)) {
|
|
1151
|
+
return body
|
|
1152
|
+
}
|
|
1153
|
+
const normalizedUserScope = normalizeUserScope(body.options.user_scope)
|
|
1154
|
+
if (!normalizedUserScope.changed) return body
|
|
1106
1155
|
return {
|
|
1107
1156
|
...body,
|
|
1108
1157
|
options: {
|
|
1109
1158
|
...body.options,
|
|
1110
|
-
user_scope:
|
|
1159
|
+
user_scope: normalizedUserScope.value,
|
|
1111
1160
|
},
|
|
1112
1161
|
}
|
|
1113
1162
|
}
|
|
1114
1163
|
|
|
1164
|
+
function validateAccessRulePolicyShape(
|
|
1165
|
+
scope: CommandScope,
|
|
1166
|
+
command: NonNullable<ReturnType<typeof findCommand>>,
|
|
1167
|
+
requestType: string,
|
|
1168
|
+
policy: Record<string, unknown> | undefined,
|
|
1169
|
+
env: EnvInput,
|
|
1170
|
+
pathPrefix: string,
|
|
1171
|
+
) {
|
|
1172
|
+
const actions = policy && Array.isArray(policy.actions) ? policy.actions : []
|
|
1173
|
+
if (actions.includes('read') || actions.includes('write')) {
|
|
1174
|
+
throwBodyValidationFailure(
|
|
1175
|
+
'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"',
|
|
1176
|
+
requestType,
|
|
1177
|
+
`${pathPrefix}.actions`,
|
|
1178
|
+
scope,
|
|
1179
|
+
command,
|
|
1180
|
+
env,
|
|
1181
|
+
)
|
|
1182
|
+
}
|
|
1183
|
+
const fields = policy && Array.isArray(policy.fields) ? policy.fields : []
|
|
1184
|
+
const allowedSystemFieldKeys = new Set(['created', 'updated', 'creator'])
|
|
1185
|
+
for (const [index, field] of fields.entries()) {
|
|
1186
|
+
if (!isRecord(field)) continue
|
|
1187
|
+
if (typeof field.key === 'number' || (typeof field.key === 'string' && /^\d+$/.test(field.key))) {
|
|
1188
|
+
throwBodyValidationFailure(
|
|
1189
|
+
'access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers',
|
|
1190
|
+
requestType,
|
|
1191
|
+
`${pathPrefix}.fields.${index}.key`,
|
|
1192
|
+
scope,
|
|
1193
|
+
command,
|
|
1194
|
+
env,
|
|
1195
|
+
)
|
|
1196
|
+
}
|
|
1197
|
+
if (typeof field.key === 'string' && !field.key.startsWith(':') && !allowedSystemFieldKeys.has(field.key)) {
|
|
1198
|
+
throwBodyValidationFailure(
|
|
1199
|
+
'access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"',
|
|
1200
|
+
requestType,
|
|
1201
|
+
`${pathPrefix}.fields.${index}.key`,
|
|
1202
|
+
scope,
|
|
1203
|
+
command,
|
|
1204
|
+
env,
|
|
1205
|
+
)
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1115
1210
|
function requireUpdateContains(
|
|
1116
1211
|
scope: CommandScope,
|
|
1117
1212
|
command: NonNullable<ReturnType<typeof findCommand>>,
|
|
@@ -1172,6 +1267,54 @@ function validateActionSpecificRawBody(
|
|
|
1172
1267
|
}
|
|
1173
1268
|
|
|
1174
1269
|
if (isAccessRuleCommand(scope, command)) {
|
|
1270
|
+
if (isAccessRuleBulkApplyCommand(scope, command)) {
|
|
1271
|
+
if (!Array.isArray(body.items) || body.items.length === 0) {
|
|
1272
|
+
throwBodyValidationFailure(
|
|
1273
|
+
'access-rule bulk-apply requires non-empty body.items; put every table/rule assignment in body.items',
|
|
1274
|
+
command.requestType,
|
|
1275
|
+
'body.items',
|
|
1276
|
+
scope,
|
|
1277
|
+
command,
|
|
1278
|
+
env,
|
|
1279
|
+
)
|
|
1280
|
+
}
|
|
1281
|
+
if (body.mode !== undefined && body.mode !== 'update_existing' && body.mode !== 'upsert_by_name') {
|
|
1282
|
+
throwBodyValidationFailure(
|
|
1283
|
+
'access-rule bulk-apply body.mode must be "update_existing" or "upsert_by_name"',
|
|
1284
|
+
command.requestType,
|
|
1285
|
+
'body.mode',
|
|
1286
|
+
scope,
|
|
1287
|
+
command,
|
|
1288
|
+
env,
|
|
1289
|
+
)
|
|
1290
|
+
}
|
|
1291
|
+
for (const [index, item] of body.items.entries()) {
|
|
1292
|
+
if (!isRecord(item)) {
|
|
1293
|
+
throwBodyValidationFailure(
|
|
1294
|
+
'access-rule bulk-apply body.items[] must be an object',
|
|
1295
|
+
command.requestType,
|
|
1296
|
+
`body.items.${index}`,
|
|
1297
|
+
scope,
|
|
1298
|
+
command,
|
|
1299
|
+
env,
|
|
1300
|
+
)
|
|
1301
|
+
}
|
|
1302
|
+
const options = isRecord(item.options) ? item.options : undefined
|
|
1303
|
+
if (!options) {
|
|
1304
|
+
throwBodyValidationFailure(
|
|
1305
|
+
'access-rule bulk-apply body.items[].options is required and must include the full canonical options object',
|
|
1306
|
+
command.requestType,
|
|
1307
|
+
`body.items.${index}.options`,
|
|
1308
|
+
scope,
|
|
1309
|
+
command,
|
|
1310
|
+
env,
|
|
1311
|
+
)
|
|
1312
|
+
}
|
|
1313
|
+
validateAccessRuleUserScope(scope, command, command.requestType, options.user_scope, env, `body.items.${index}.options.user_scope`)
|
|
1314
|
+
validateAccessRulePolicyShape(scope, command, command.requestType, isRecord(options.policy) ? options.policy : undefined, env, `body.items.${index}.options.policy`)
|
|
1315
|
+
}
|
|
1316
|
+
return
|
|
1317
|
+
}
|
|
1175
1318
|
const update = stringArray(body.update)
|
|
1176
1319
|
if (isAccessRuleUpdateCommand(scope, command) && update.some((item) => item.startsWith('options.'))) {
|
|
1177
1320
|
throwBodyValidationFailure(
|
|
@@ -1216,42 +1359,7 @@ function validateActionSpecificRawBody(
|
|
|
1216
1359
|
)
|
|
1217
1360
|
}
|
|
1218
1361
|
validateAccessRuleUserScope(scope, command, command.requestType, options?.user_scope, env)
|
|
1219
|
-
|
|
1220
|
-
if (actions.includes('read') || actions.includes('write')) {
|
|
1221
|
-
throwBodyValidationFailure(
|
|
1222
|
-
'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"',
|
|
1223
|
-
command.requestType,
|
|
1224
|
-
'body.options.policy.actions',
|
|
1225
|
-
scope,
|
|
1226
|
-
command,
|
|
1227
|
-
env,
|
|
1228
|
-
)
|
|
1229
|
-
}
|
|
1230
|
-
const fields = policy && Array.isArray(policy.fields) ? policy.fields : []
|
|
1231
|
-
const allowedSystemFieldKeys = new Set(['created', 'updated', 'creator'])
|
|
1232
|
-
for (const [index, field] of fields.entries()) {
|
|
1233
|
-
if (!isRecord(field)) continue
|
|
1234
|
-
if (typeof field.key === 'number' || (typeof field.key === 'string' && /^\d+$/.test(field.key))) {
|
|
1235
|
-
throwBodyValidationFailure(
|
|
1236
|
-
'access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers',
|
|
1237
|
-
command.requestType,
|
|
1238
|
-
`body.options.policy.fields.${index}.key`,
|
|
1239
|
-
scope,
|
|
1240
|
-
command,
|
|
1241
|
-
env,
|
|
1242
|
-
)
|
|
1243
|
-
}
|
|
1244
|
-
if (typeof field.key === 'string' && !field.key.startsWith(':') && !allowedSystemFieldKeys.has(field.key)) {
|
|
1245
|
-
throwBodyValidationFailure(
|
|
1246
|
-
'access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"',
|
|
1247
|
-
command.requestType,
|
|
1248
|
-
`body.options.policy.fields.${index}.key`,
|
|
1249
|
-
scope,
|
|
1250
|
-
command,
|
|
1251
|
-
env,
|
|
1252
|
-
)
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1362
|
+
validateAccessRulePolicyShape(scope, command, command.requestType, policy, env, 'body.options.policy')
|
|
1255
1363
|
}
|
|
1256
1364
|
}
|
|
1257
1365
|
|
|
@@ -109,6 +109,10 @@ const docHintIndex: Record<string, RuntimeDocHint[]> = {
|
|
|
109
109
|
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
110
110
|
examplesIndexDocHint,
|
|
111
111
|
],
|
|
112
|
+
AppIngressBulkApplyReqVO: [
|
|
113
|
+
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
114
|
+
examplesIndexDocHint,
|
|
115
|
+
],
|
|
112
116
|
EntitySaveReqVO: [
|
|
113
117
|
{ title: 'Table schema cheatsheet', file: 'docs/runtime-reference/entity-schema.md' },
|
|
114
118
|
{ title: 'Field type index', file: 'docs/runtime-reference/entity-schema/README.md' },
|
|
@@ -225,6 +229,10 @@ const commandDocHintIndex: Record<string, RuntimeDocHint[]> = {
|
|
|
225
229
|
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
226
230
|
examplesIndexDocHint,
|
|
227
231
|
],
|
|
232
|
+
'admin.access-rule.bulkApplyEntityIngress': [
|
|
233
|
+
{ title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
|
|
234
|
+
examplesIndexDocHint,
|
|
235
|
+
],
|
|
228
236
|
'user.row.insertEntity': [
|
|
229
237
|
{ title: 'Row CRUD', file: 'docs/runtime-reference/row-crud.md' },
|
|
230
238
|
{ title: 'Uploads', file: 'docs/runtime-reference/uploads.md' },
|
|
@@ -1387,6 +1387,92 @@ test('access-rule assign-users accepts canonical mixed user_scope body', async (
|
|
|
1387
1387
|
])
|
|
1388
1388
|
})
|
|
1389
1389
|
|
|
1390
|
+
test('access-rule bulk-apply normalizes numeric string ids before validation and request', async () => {
|
|
1391
|
+
const body = JSON.stringify({
|
|
1392
|
+
mode: 'update_existing',
|
|
1393
|
+
items: [
|
|
1394
|
+
{
|
|
1395
|
+
table_id: '2188893443',
|
|
1396
|
+
rule_id: '2188893557',
|
|
1397
|
+
enabled: true,
|
|
1398
|
+
options: {
|
|
1399
|
+
user_scope: [{ type: 'department', id: '2188881201' }],
|
|
1400
|
+
policy: { key: 'custom', actions: ['view'], all_fields_read: true, all_fields_write: false },
|
|
1401
|
+
list_options: {},
|
|
1402
|
+
},
|
|
1403
|
+
},
|
|
1404
|
+
],
|
|
1405
|
+
})
|
|
1406
|
+
let requestedURL = ''
|
|
1407
|
+
const out = await executeCLI(
|
|
1408
|
+
'admin',
|
|
1409
|
+
['access-rule', 'bulk-apply', '--app-id', 'app_1', '--body-json', body],
|
|
1410
|
+
env as any,
|
|
1411
|
+
async (url, init) => {
|
|
1412
|
+
requestedURL = String(url)
|
|
1413
|
+
return new Response(String(init?.body ?? '{}'), { status: 200 })
|
|
1414
|
+
},
|
|
1415
|
+
)
|
|
1416
|
+
assert.equal(out.kind, 'result')
|
|
1417
|
+
assert.match(requestedURL, /\/api\/apps\/app_1\/access-rules\/bulk-apply$/)
|
|
1418
|
+
assert.equal(out.data.items[0].table_id, 2188893443)
|
|
1419
|
+
assert.equal(out.data.items[0].rule_id, 2188893557)
|
|
1420
|
+
assert.deepEqual(out.data.items[0].options.user_scope, [{ type: 'department', id: 2188881201 }])
|
|
1421
|
+
})
|
|
1422
|
+
|
|
1423
|
+
test('access-rule bulk-apply rejects empty items before request', async () => {
|
|
1424
|
+
await assert.rejects(async () => {
|
|
1425
|
+
await executeCLI(
|
|
1426
|
+
'admin',
|
|
1427
|
+
['access-rule', 'bulk-apply', '--app-id', 'app_1', '--body-json', '{"mode":"upsert_by_name","items":[]}'],
|
|
1428
|
+
env as any,
|
|
1429
|
+
async () => new Response('{}', { status: 200 }),
|
|
1430
|
+
)
|
|
1431
|
+
}, (error: unknown) => {
|
|
1432
|
+
assert.ok(error instanceof CLIError)
|
|
1433
|
+
assert.equal(error.code, 'BODY_VALIDATION_FAILED')
|
|
1434
|
+
assert.equal(error.details?.requestType, 'AppIngressBulkApplyReqVO')
|
|
1435
|
+
assert.match(error.message, /non-empty body.items/)
|
|
1436
|
+
return true
|
|
1437
|
+
})
|
|
1438
|
+
})
|
|
1439
|
+
|
|
1440
|
+
test('access-rule bulk-apply rejects non-canonical policy actions before request', async () => {
|
|
1441
|
+
const body = JSON.stringify({
|
|
1442
|
+
mode: 'upsert_by_name',
|
|
1443
|
+
items: [
|
|
1444
|
+
{
|
|
1445
|
+
table_id: 2188893443,
|
|
1446
|
+
name: 'Bad read alias',
|
|
1447
|
+
enabled: true,
|
|
1448
|
+
options: {
|
|
1449
|
+
user_scope: [{ type: 'user', id: 2188889901 }],
|
|
1450
|
+
policy: { key: 'custom', actions: ['read'], all_fields_read: true, all_fields_write: false },
|
|
1451
|
+
list_options: {},
|
|
1452
|
+
},
|
|
1453
|
+
},
|
|
1454
|
+
],
|
|
1455
|
+
})
|
|
1456
|
+
await assert.rejects(async () => {
|
|
1457
|
+
await executeCLI(
|
|
1458
|
+
'admin',
|
|
1459
|
+
['access-rule', 'bulk-apply', '--app-id', 'app_1', '--body-json', body],
|
|
1460
|
+
env as any,
|
|
1461
|
+
async () => new Response('{}', { status: 200 }),
|
|
1462
|
+
)
|
|
1463
|
+
}, (error: unknown) => {
|
|
1464
|
+
assert.ok(error instanceof CLIError)
|
|
1465
|
+
assert.equal(error.code, 'BODY_VALIDATION_FAILED')
|
|
1466
|
+
assert.equal(error.details?.requestType, 'AppIngressBulkApplyReqVO')
|
|
1467
|
+
assert.match(error.message, /actions must use Arcubase action codes/)
|
|
1468
|
+
assert.deepEqual(error.details?.issues?.[0], {
|
|
1469
|
+
path: 'body.items.0.options.policy.actions',
|
|
1470
|
+
message: '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"',
|
|
1471
|
+
})
|
|
1472
|
+
return true
|
|
1473
|
+
})
|
|
1474
|
+
})
|
|
1475
|
+
|
|
1390
1476
|
test('access-rule create accepts nested custom policy condition with field columns', async () => {
|
|
1391
1477
|
const body = JSON.stringify({
|
|
1392
1478
|
name: 'Sales own or west region',
|
package/src/tests/help.test.ts
CHANGED
|
@@ -153,6 +153,19 @@ test('admin access-rule create help-examples show canonical ingress scopes and c
|
|
|
153
153
|
assert.doesNotMatch(out.text, /"type":"member"/)
|
|
154
154
|
})
|
|
155
155
|
|
|
156
|
+
test('admin access-rule bulk-apply help-examples show transactional matrix body', async () => {
|
|
157
|
+
const out = await executeCLI('admin', ['access-rule', 'bulk-apply', '--help-examples'], env as any, async () => new Response('{}'))
|
|
158
|
+
assert.equal(out.kind, 'help')
|
|
159
|
+
assert.match(out.text, /arcubase-admin access-rule bulk-apply --help-examples/)
|
|
160
|
+
assert.match(out.text, /"mode":"upsert_by_name"/)
|
|
161
|
+
assert.match(out.text, /"table_id":2188893443/)
|
|
162
|
+
assert.match(out.text, /"type":"department"/)
|
|
163
|
+
assert.match(out.text, /"type":"actor"/)
|
|
164
|
+
assert.match(out.text, /"value":"\[\[current_user\]\]"/)
|
|
165
|
+
assert.doesNotMatch(out.text, forbiddenArcubaseUserTerms())
|
|
166
|
+
assert.doesNotMatch(out.text, /"type":"member"/)
|
|
167
|
+
})
|
|
168
|
+
|
|
156
169
|
test('user row bulk-update help-examples show ids and condition selection bodies', async () => {
|
|
157
170
|
const out = await executeCLI('user', ['row', 'bulk-update', '--help-examples'], env as any, async () => new Response('{}'))
|
|
158
171
|
assert.equal(out.kind, 'help')
|