@carthooks/arcubase-cli 0.1.22 → 0.1.24

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.
@@ -1095,6 +1095,16 @@ test('entry fixed app tables query cannot be disabled by query file', async () =
1095
1095
  })
1096
1096
 
1097
1097
  test('dashboard and widget commands map to approved endpoints', async () => {
1098
+ const userAppGet = await executeCLI(
1099
+ 'user',
1100
+ ['app', 'get', '--app-id', 'app_1'],
1101
+ env as any,
1102
+ async (url, init) => new Response(JSON.stringify({ url, method: init?.method }), { status: 200 }),
1103
+ )
1104
+ assert.equal(userAppGet.kind, 'result')
1105
+ assert.equal(userAppGet.data.url, 'https://arcubase.example.com/api/apps/app_1')
1106
+ assert.equal(userAppGet.data.method, 'GET')
1107
+
1098
1108
  const adminWidgetList = await executeCLI(
1099
1109
  'admin',
1100
1110
  ['widget', 'list', '--app-id', 'app_1', '--type', 'dashboard', '--dashboard-id', '100'],
@@ -1131,6 +1141,61 @@ test('dashboard and widget commands map to approved endpoints', async () => {
1131
1141
  assert.deepEqual(userWidgetData.data.body, { dashboard_id: 100, type: 1 })
1132
1142
  })
1133
1143
 
1144
+ test('widget create ingress mistakes suggest ingress widget shape before request', async () => {
1145
+ let called = false
1146
+ await assert.rejects(async () => {
1147
+ await executeCLI(
1148
+ 'admin',
1149
+ ['widget', 'create', '--app-id', 'app_1', '--body-json', '{"type":"list","options":{"entity_id":2188891001}}'],
1150
+ env as any,
1151
+ async () => {
1152
+ called = true
1153
+ return new Response('{}', { status: 200 })
1154
+ },
1155
+ )
1156
+ }, (error: unknown) => {
1157
+ assert.ok(error instanceof CLIError)
1158
+ assert.equal(error.code, 'BODY_VALIDATION_FAILED')
1159
+ assert.equal(error.details?.requestType, 'AppNewWidgetsReqVO')
1160
+ assert.ok((error.details?.commonMistakes ?? []).includes('ingress widgets use options.ingress_id'))
1161
+ assert.ok((error.details?.commonMistakes ?? []).includes('after creating an ingress widget, use widget update-ingress-options to adjust display configuration'))
1162
+ return true
1163
+ })
1164
+ assert.equal(called, false)
1165
+ })
1166
+
1167
+ test('widget update ingress list options points to update-ingress-options before request', async () => {
1168
+ let called = false
1169
+ await assert.rejects(async () => {
1170
+ await executeCLI(
1171
+ 'admin',
1172
+ [
1173
+ 'widget',
1174
+ 'update',
1175
+ '--app-id',
1176
+ 'app_1',
1177
+ '--widget-id',
1178
+ 'widget_1',
1179
+ '--body-json',
1180
+ '{"ingress_id":2188893001,"options":{"list":{"default_cols":[1001,1002],"page_size":10}}}',
1181
+ ],
1182
+ env as any,
1183
+ async () => {
1184
+ called = true
1185
+ return new Response('{}', { status: 200 })
1186
+ },
1187
+ )
1188
+ }, (error: unknown) => {
1189
+ assert.ok(error instanceof CLIError)
1190
+ assert.equal(error.code, 'BODY_VALIDATION_FAILED')
1191
+ assert.equal(error.details?.requestType, 'AppUpdateWidgetsReqVO')
1192
+ assert.ok((error.details?.commonMistakes ?? []).includes('ingress/list display settings use widget update-ingress-options, not widget update'))
1193
+ assert.ok((error.details?.suggestions ?? []).some((item) => item.includes('widget update-ingress-options')))
1194
+ return true
1195
+ })
1196
+ assert.equal(called, false)
1197
+ })
1198
+
1134
1199
  test('widget preview-data rejects unsupported aggregate before request', async () => {
1135
1200
  let called = false
1136
1201
  await assert.rejects(async () => {
@@ -1191,6 +1256,38 @@ test('widget preview-data rejects field dimensions without IsField true before r
1191
1256
  assert.equal(called, false)
1192
1257
  })
1193
1258
 
1259
+ test('widget preview-data filter mistakes suggest chart conditions before request', async () => {
1260
+ let called = false
1261
+ await assert.rejects(async () => {
1262
+ await executeCLI(
1263
+ 'admin',
1264
+ [
1265
+ 'widget',
1266
+ 'preview-data',
1267
+ '--app-id',
1268
+ 'app_1',
1269
+ '--body-json',
1270
+ '{"dataSource":2188891001,"dataSourceType":"entity","dimensions":[{"IsField":true,"FieldID":1004,"Name":"Source"}],"filters":[{"FieldID":1006,"Operator":"$eq","Value":"yes"}],"measures":[{"Name":"Distinct Customers","FieldID":1002,"Aggregate":"distinct_count"}]}',
1271
+ ],
1272
+ env as any,
1273
+ async () => {
1274
+ called = true
1275
+ return new Response('{}', { status: 200 })
1276
+ },
1277
+ )
1278
+ }, (error: unknown) => {
1279
+ assert.ok(error instanceof CLIError)
1280
+ assert.equal(error.code, 'BODY_VALIDATION_FAILED')
1281
+ assert.equal(error.details?.requestType, 'AppPreviewWidgetsDataReqVO')
1282
+ assert.ok((error.details?.issues ?? []).some((item) => item.path === 'body'))
1283
+ assert.ok((error.details?.commonMistakes ?? []).includes('use conditions, not filters, for chart data filtering'))
1284
+ assert.ok((error.details?.commonMistakes ?? []).includes('do not retry without conditions and then hand-calculate filtered statistics'))
1285
+ assert.ok((error.details?.suggestions ?? []).some((item) => item.includes('"conditions":{"mode":"and","conditions":[{"column":":1006","operator":"$eq","value":"yes"}]}')))
1286
+ return true
1287
+ })
1288
+ assert.equal(called, false)
1289
+ })
1290
+
1194
1291
  test('widget update rejects chart field dimensions without IsField true before request', async () => {
1195
1292
  let called = false
1196
1293
  await assert.rejects(async () => {
@@ -136,6 +136,12 @@ test('dashboard and widget help exposes agent-readable body docs and flags', asy
136
136
  assert.match(userWidgetData.text, /body: FetchWidgetsDataReqVO via --body-json/)
137
137
  assert.match(userWidgetData.text, /^types:$/m)
138
138
  assert.match(userWidgetData.text, /^docs:$/m)
139
+
140
+ const userAppGet = await executeCLI('user', ['app', 'get', '--help'], env as any, async () => new Response('{}'))
141
+ assert.equal(userAppGet.kind, 'help')
142
+ assert.match(userAppGet.text, /arcubase app get/)
143
+ assert.match(userAppGet.text, /endpoint: \/apps\/:id/)
144
+ assert.match(userAppGet.text, /path flags: --app-id/)
139
145
  })
140
146
 
141
147
  test('admin access-rule help gives update whitelist and assign-users body-json examples', async () => {
@@ -214,6 +220,17 @@ test('admin dashboard update-layout help-examples show top-level layout array',
214
220
  assert.match(out.text, /do not wrap the array/)
215
221
  })
216
222
 
223
+ test('admin dashboard update-options help-examples show user visibility scope', async () => {
224
+ const out = await executeCLI('admin', ['dashboard', 'update-options', '--help-examples'], env as any, async () => new Response('{}'))
225
+ assert.equal(out.kind, 'help')
226
+ assert.match(out.text, /arcubase-admin dashboard update-options --help-examples/)
227
+ assert.match(out.text, /"changed":\["users"\]/)
228
+ assert.match(out.text, /"users":\[\{"type":"user","id":2188889901\}\]/)
229
+ assert.match(out.text, /currentSpeaker\.arcubaseUserId/)
230
+ assert.match(out.text, /verify from the user side with arcubase app get/)
231
+ assert.doesNotMatch(out.text, /"type":"member"/)
232
+ })
233
+
217
234
  test('admin widget preview-data help-examples show chart query body without widget id', async () => {
218
235
  const out = await executeCLI('admin', ['widget', 'preview-data', '--help-examples'], env as any, async () => new Response('{}'))
219
236
  assert.equal(out.kind, 'help')
@@ -221,6 +238,8 @@ test('admin widget preview-data help-examples show chart query body without widg
221
238
  assert.match(out.text, /"dataSource":2188891001/)
222
239
  assert.match(out.text, /"dataSourceType":"entity"/)
223
240
  assert.match(out.text, /"Aggregate":"sum"/)
241
+ assert.match(out.text, /"conditions":\{"mode":"and","conditions":\[\{"column":":1006","operator":"\$eq","value":"yes"\}\]\}/)
242
+ assert.match(out.text, /use conditions, not filters/)
224
243
  assert.match(out.text, /preview-data does not accept --widget-id/)
225
244
  assert.match(out.text, /do not send widget_id or id/)
226
245
  assert.doesNotMatch(out.text, /--widget-id <widget_id>/)
@@ -236,6 +255,14 @@ test('admin widget create help-examples avoid manual options type', async () =>
236
255
  assert.doesNotMatch(out.text, /"options":\{"dashboard_id":2188892001,"type":/)
237
256
  })
238
257
 
258
+ test('admin widget update-ingress-options help-examples show ingress display body', async () => {
259
+ const out = await executeCLI('admin', ['widget', 'update-ingress-options', '--help-examples'], env as any, async () => new Response('{}'))
260
+ assert.equal(out.kind, 'help')
261
+ assert.match(out.text, /arcubase-admin widget update-ingress-options --help-examples/)
262
+ assert.match(out.text, /"cond_follow":true/)
263
+ assert.match(out.text, /verify with widget get/)
264
+ })
265
+
239
266
  test('user row bulk-update help-examples show ids and condition selection bodies', async () => {
240
267
  const out = await executeCLI('user', ['row', 'bulk-update', '--help-examples'], env as any, async () => new Response('{}'))
241
268
  assert.equal(out.kind, 'help')