@ductape/cli 0.3.17 → 0.3.19
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/dist/commands/app-actions.d.ts +8 -0
- package/dist/commands/app-actions.js +56 -0
- package/dist/commands/db.d.ts +18 -0
- package/dist/commands/db.js +67 -1
- package/dist/index.js +82 -1
- package/dist/lib/platform-api.d.ts +5 -0
- package/dist/lib/platform-api.js +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type CommandInteractiveFlags } from '../lib/interactive-opts.js';
|
|
2
|
+
export declare function runAppActions(verb: string, opts: {
|
|
3
|
+
profile?: string;
|
|
4
|
+
app?: string;
|
|
5
|
+
action?: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
} & CommandInteractiveFlags): Promise<void>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { toInteractiveOpts } from '../lib/interactive-opts.js';
|
|
2
|
+
import { bodyRequiredHint, resolveBody } from '../lib/read-body.js';
|
|
3
|
+
import { createAppAction, deleteAppAction, getAppAction, listAppActions, updateAppAction, } from '../lib/platform-api.js';
|
|
4
|
+
import { printJson } from '../lib/output.js';
|
|
5
|
+
import { requireWorkspaceContext } from '../lib/workspace-context.js';
|
|
6
|
+
const VERBS = ['list', 'get', 'create', 'update', 'delete'];
|
|
7
|
+
export async function runAppActions(verb, opts) {
|
|
8
|
+
const v = verb.toLowerCase();
|
|
9
|
+
if (!VERBS.includes(v)) {
|
|
10
|
+
throw new Error(`Unknown apps actions verb "${verb}". Use: ${VERBS.join(', ')}`);
|
|
11
|
+
}
|
|
12
|
+
if (!opts.app)
|
|
13
|
+
throw new Error('Provide --app <app_tag>');
|
|
14
|
+
const ctx = requireWorkspaceContext({ profile: opts.profile });
|
|
15
|
+
const interactive = toInteractiveOpts(opts);
|
|
16
|
+
const body = v === 'create' || v === 'update'
|
|
17
|
+
? await resolveBody({
|
|
18
|
+
...interactive,
|
|
19
|
+
file: opts.file,
|
|
20
|
+
patch: v === 'update',
|
|
21
|
+
requireBody: v === 'create',
|
|
22
|
+
})
|
|
23
|
+
: undefined;
|
|
24
|
+
let result;
|
|
25
|
+
switch (v) {
|
|
26
|
+
case 'list':
|
|
27
|
+
result = await listAppActions(ctx, opts.app);
|
|
28
|
+
break;
|
|
29
|
+
case 'get':
|
|
30
|
+
if (!opts.action)
|
|
31
|
+
throw new Error('Provide --action <action_tag>');
|
|
32
|
+
result = await getAppAction(ctx, opts.app, opts.action);
|
|
33
|
+
break;
|
|
34
|
+
case 'create':
|
|
35
|
+
if (!body || Object.keys(body).length === 0) {
|
|
36
|
+
throw new Error(`create requires a body. ${bodyRequiredHint('apps:actions:create')}`);
|
|
37
|
+
}
|
|
38
|
+
result = await createAppAction(ctx, opts.app, body);
|
|
39
|
+
break;
|
|
40
|
+
case 'update': {
|
|
41
|
+
const actionTag = opts.action ?? body?.tag;
|
|
42
|
+
if (!actionTag)
|
|
43
|
+
throw new Error('update requires --action <action_tag>');
|
|
44
|
+
result = await updateAppAction(ctx, opts.app, actionTag, body ?? {});
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case 'delete':
|
|
48
|
+
if (!opts.action)
|
|
49
|
+
throw new Error('Provide --action <action_tag>');
|
|
50
|
+
result = await deleteAppAction(ctx, opts.app, opts.action);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(`Unsupported: apps actions ${v}`);
|
|
54
|
+
}
|
|
55
|
+
printJson(result, Boolean(opts.json));
|
|
56
|
+
}
|
package/dist/commands/db.d.ts
CHANGED
|
@@ -2,4 +2,22 @@ export declare function runDb(method: string, opts: {
|
|
|
2
2
|
file?: string;
|
|
3
3
|
json?: boolean;
|
|
4
4
|
}): Promise<void>;
|
|
5
|
+
/**
|
|
6
|
+
* Database Actions — reusable, parameterized query/mutation definitions saved against a
|
|
7
|
+
* database component (databases.action.* in the SDK). This is administrative metadata
|
|
8
|
+
* (uses the access-key-backed sdk-proxy, same as `resources <type> create`), not a runtime
|
|
9
|
+
* query — it never connects to the live database. Required for any Feature step that calls
|
|
10
|
+
* ctx.database.query/insert/update/delete, since those take an `event` tag pointing at one
|
|
11
|
+
* of these, not a raw {table, where} shape.
|
|
12
|
+
*
|
|
13
|
+
* Tag format is always "product_tag:database_tag:action_tag" — always pass it fully
|
|
14
|
+
* qualified here (the SDK's 2-part fallback only works inside a single long-lived process
|
|
15
|
+
* that already has that product's builder cached, which a fresh CLI invocation never has).
|
|
16
|
+
*/
|
|
17
|
+
export declare function runDbActions(verb: string, opts: {
|
|
18
|
+
tag?: string;
|
|
19
|
+
database?: string;
|
|
20
|
+
file?: string;
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}, extraArgs: string[]): Promise<void>;
|
|
5
23
|
export declare function runDbContext(json: boolean): void;
|
package/dist/commands/db.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readJsonBody } from '../lib/read-body.js';
|
|
2
2
|
import { setDatabaseContext } from '../lib/context-store.js';
|
|
3
3
|
import { DB_PROXY_METHODS } from '../lib/db-methods.js';
|
|
4
|
-
import { getDbContext, getDbProxy, requireSession } from '../lib/proxy/context.js';
|
|
4
|
+
import { getDbContext, getDbProxy, getSdkProxy, requireSession } from '../lib/proxy/context.js';
|
|
5
5
|
import { loadRuntimeContext } from '../lib/context-store.js';
|
|
6
6
|
import { printJson } from '../lib/output.js';
|
|
7
7
|
export async function runDb(method, opts) {
|
|
@@ -47,6 +47,72 @@ export async function runDb(method, opts) {
|
|
|
47
47
|
}
|
|
48
48
|
printJson(result, Boolean(opts.json));
|
|
49
49
|
}
|
|
50
|
+
const DB_ACTION_VERBS = ['create', 'update', 'get', 'list', 'delete'];
|
|
51
|
+
/**
|
|
52
|
+
* Database Actions — reusable, parameterized query/mutation definitions saved against a
|
|
53
|
+
* database component (databases.action.* in the SDK). This is administrative metadata
|
|
54
|
+
* (uses the access-key-backed sdk-proxy, same as `resources <type> create`), not a runtime
|
|
55
|
+
* query — it never connects to the live database. Required for any Feature step that calls
|
|
56
|
+
* ctx.database.query/insert/update/delete, since those take an `event` tag pointing at one
|
|
57
|
+
* of these, not a raw {table, where} shape.
|
|
58
|
+
*
|
|
59
|
+
* Tag format is always "product_tag:database_tag:action_tag" — always pass it fully
|
|
60
|
+
* qualified here (the SDK's 2-part fallback only works inside a single long-lived process
|
|
61
|
+
* that already has that product's builder cached, which a fresh CLI invocation never has).
|
|
62
|
+
*/
|
|
63
|
+
export async function runDbActions(verb, opts, extraArgs) {
|
|
64
|
+
const v = verb.toLowerCase();
|
|
65
|
+
if (!DB_ACTION_VERBS.includes(v)) {
|
|
66
|
+
throw new Error(`Unknown db actions verb "${verb}". Use: ${DB_ACTION_VERBS.join(', ')}`);
|
|
67
|
+
}
|
|
68
|
+
const proxy = getSdkProxy();
|
|
69
|
+
const tag = opts.tag ?? extraArgs[0];
|
|
70
|
+
let method;
|
|
71
|
+
let params;
|
|
72
|
+
switch (v) {
|
|
73
|
+
case 'create': {
|
|
74
|
+
const body = readJsonBody(opts.file);
|
|
75
|
+
if (!body || Object.keys(body).length === 0) {
|
|
76
|
+
throw new Error('create requires a body: { tag: "product:database:action", name, tableName, ' +
|
|
77
|
+
'operation, template, description?, filterTemplate? } (pass -f <file.json>).');
|
|
78
|
+
}
|
|
79
|
+
method = 'action.create';
|
|
80
|
+
params = [body];
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'update': {
|
|
84
|
+
if (!tag)
|
|
85
|
+
throw new Error('update requires the action tag ("product:database:action")');
|
|
86
|
+
const body = readJsonBody(opts.file) ?? {};
|
|
87
|
+
method = 'action.update';
|
|
88
|
+
params = [{ tag, ...body }];
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case 'get':
|
|
92
|
+
if (!tag)
|
|
93
|
+
throw new Error('get requires the action tag ("product:database:action")');
|
|
94
|
+
method = 'action.fetch';
|
|
95
|
+
params = [tag];
|
|
96
|
+
break;
|
|
97
|
+
case 'list': {
|
|
98
|
+
const databaseTag = opts.database ?? tag;
|
|
99
|
+
if (!databaseTag) {
|
|
100
|
+
throw new Error('list requires --database <product_tag:database_tag>');
|
|
101
|
+
}
|
|
102
|
+
method = 'action.fetchAll';
|
|
103
|
+
params = [databaseTag];
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
case 'delete':
|
|
107
|
+
if (!tag)
|
|
108
|
+
throw new Error('delete requires the action tag ("product:database:action")');
|
|
109
|
+
method = 'action.delete';
|
|
110
|
+
params = [tag];
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
const result = await proxy.execute('databases', method, params);
|
|
114
|
+
printJson(result, Boolean(opts.json));
|
|
115
|
+
}
|
|
50
116
|
export function runDbContext(json) {
|
|
51
117
|
const session = requireSession();
|
|
52
118
|
const ctx = getDbContext(session);
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import { runInstall } from './commands/install.js';
|
|
|
15
15
|
import { runStart, runStop, runStatus } from './commands/platform.js';
|
|
16
16
|
import { runResourceCrud, runResourcesList, runEventTopicCrud, runNotificationMessageCrud } from './commands/resources.js';
|
|
17
17
|
import { runCloud, runCloudPreflight } from './commands/cloud.js';
|
|
18
|
-
import { runDb, runDbContext } from './commands/db.js';
|
|
18
|
+
import { runDb, runDbActions, runDbContext } from './commands/db.js';
|
|
19
19
|
import { runDbMigrate, runDbMigrateStatus, runDbMigrateRollback } from './commands/db-migrate.js';
|
|
20
20
|
import { runFeaturesSync } from './commands/features-sync.js';
|
|
21
21
|
import { runDbSchemaGenerate, runDbSchemaPush } from './commands/db-schema.js';
|
|
@@ -27,6 +27,7 @@ import { runCompletion } from './commands/completion.js';
|
|
|
27
27
|
import { runProducts, runProductApps, runProductAppActions, runProductComponents, runProductEnvironments } from './commands/products.js';
|
|
28
28
|
import { runApps } from './commands/apps.js';
|
|
29
29
|
import { runAppsImport } from './commands/apps-import.js';
|
|
30
|
+
import { runAppActions } from './commands/app-actions.js';
|
|
30
31
|
import { runMarketplace } from './commands/marketplace.js';
|
|
31
32
|
import { runApply } from './commands/apply.js';
|
|
32
33
|
import { runMigrateCodebase } from './commands/migrate-codebase.js';
|
|
@@ -663,6 +664,49 @@ apps
|
|
|
663
664
|
version: opts.version,
|
|
664
665
|
json: opts.json,
|
|
665
666
|
})));
|
|
667
|
+
const appActions = apps.command('actions').description("An app's own actions (endpoints). resource is a path relative to each of the app's " +
|
|
668
|
+
'environment base_urls (e.g. "/v1/users/{id}"), not a full URL.');
|
|
669
|
+
appActions
|
|
670
|
+
.command('list')
|
|
671
|
+
.requiredOption('--app <tag>', 'App tag')
|
|
672
|
+
.option('--profile <name>')
|
|
673
|
+
.option('--json', 'JSON output')
|
|
674
|
+
.action(wrap((opts) => runAppActions('list', opts)));
|
|
675
|
+
appActions
|
|
676
|
+
.command('get')
|
|
677
|
+
.requiredOption('--app <tag>', 'App tag')
|
|
678
|
+
.requiredOption('--action <tag>', 'Action tag')
|
|
679
|
+
.option('--profile <name>')
|
|
680
|
+
.option('--json', 'JSON output')
|
|
681
|
+
.action(wrap((opts) => runAppActions('get', opts)));
|
|
682
|
+
appActions
|
|
683
|
+
.command('create')
|
|
684
|
+
.requiredOption('--app <tag>', 'App tag')
|
|
685
|
+
// NOTE: cannot use -f/--file here — it collides with the parent `apps` command's own -f/--file
|
|
686
|
+
// option (used by `apps create`/`apps update`). Commander resolves a flag shared by an ancestor
|
|
687
|
+
// and a descendant command against the ancestor, so the value never reaches this subcommand's
|
|
688
|
+
// own opts, and requiredOption fails even when -f is supplied. Same fix as productEnvironments
|
|
689
|
+
// create/update (--env-file) above.
|
|
690
|
+
.requiredOption('--action-file <path>', 'JSON body: { tag, name, resource, method, description?, params?, query?, headers?, body?, response? }')
|
|
691
|
+
.option('--profile <name>')
|
|
692
|
+
.option('--json', 'JSON output')
|
|
693
|
+
.action(wrap((opts) => runAppActions('create', { ...opts, file: opts.actionFile })));
|
|
694
|
+
appActions
|
|
695
|
+
.command('update')
|
|
696
|
+
.requiredOption('--app <tag>', 'App tag')
|
|
697
|
+
.requiredOption('--action <tag>', 'Action tag')
|
|
698
|
+
// See NOTE above on `create` — same -f/--file collision with the parent `apps` command.
|
|
699
|
+
.requiredOption('--action-file <path>', 'JSON body (partial update)')
|
|
700
|
+
.option('--profile <name>')
|
|
701
|
+
.option('--json', 'JSON output')
|
|
702
|
+
.action(wrap((opts) => runAppActions('update', { ...opts, file: opts.actionFile })));
|
|
703
|
+
appActions
|
|
704
|
+
.command('delete')
|
|
705
|
+
.requiredOption('--app <tag>', 'App tag')
|
|
706
|
+
.requiredOption('--action <tag>', 'Action tag')
|
|
707
|
+
.option('--profile <name>')
|
|
708
|
+
.option('--json', 'JSON output')
|
|
709
|
+
.action(wrap((opts) => runAppActions('delete', opts)));
|
|
666
710
|
program
|
|
667
711
|
.command('marketplace')
|
|
668
712
|
.description('Discover public apps by capability and inspect their actions')
|
|
@@ -761,6 +805,43 @@ dbMigrate
|
|
|
761
805
|
.option('--db <tag>', 'Limit to one db entry')
|
|
762
806
|
.option('-n <count>', 'Number of migrations to roll back', '1')
|
|
763
807
|
.action(wrap((opts) => runDbMigrateRollback({ env: opts.env, db: opts.db, n: Number(opts.n) })));
|
|
808
|
+
const dbActions = db
|
|
809
|
+
.command('actions')
|
|
810
|
+
.description('Reusable database action definitions (saved parameterized queries). Required for any ' +
|
|
811
|
+
'Feature step that uses ctx.database.query/insert/update/delete — those take an `event` ' +
|
|
812
|
+
'tag pointing at one of these, not a raw table/where shape. Administrative (access-key), ' +
|
|
813
|
+
'not a runtime query — create/update never touch the live database.');
|
|
814
|
+
dbActions
|
|
815
|
+
.command('create')
|
|
816
|
+
.description('Create a database action from a file: { tag: "product:database:action", name, tableName, operation, template }')
|
|
817
|
+
.requiredOption('-f, --file <path>', 'JSON file with the action definition')
|
|
818
|
+
.option('--json', 'JSON output')
|
|
819
|
+
.action(wrap((opts) => runDbActions('create', { file: opts.file, json: opts.json }, [])));
|
|
820
|
+
dbActions
|
|
821
|
+
.command('update')
|
|
822
|
+
.description('Update an existing database action')
|
|
823
|
+
.requiredOption('-t, --tag <tag>', 'Action tag ("product:database:action")')
|
|
824
|
+
.requiredOption('-f, --file <path>', 'JSON file with the fields to change')
|
|
825
|
+
.option('--json', 'JSON output')
|
|
826
|
+
.action(wrap((opts) => runDbActions('update', { tag: opts.tag, file: opts.file, json: opts.json }, [])));
|
|
827
|
+
dbActions
|
|
828
|
+
.command('get')
|
|
829
|
+
.description('Fetch one database action')
|
|
830
|
+
.argument('<tag>', 'Action tag ("product:database:action")')
|
|
831
|
+
.option('--json', 'JSON output')
|
|
832
|
+
.action(wrap((tag, opts) => runDbActions('get', { tag, json: opts.json }, [])));
|
|
833
|
+
dbActions
|
|
834
|
+
.command('list')
|
|
835
|
+
.description('List all actions on a database')
|
|
836
|
+
.requiredOption('-d, --database <tag>', 'Database tag ("product_tag:database_tag")')
|
|
837
|
+
.option('--json', 'JSON output')
|
|
838
|
+
.action(wrap((opts) => runDbActions('list', { database: opts.database, json: opts.json }, [])));
|
|
839
|
+
dbActions
|
|
840
|
+
.command('delete')
|
|
841
|
+
.description('Delete a database action')
|
|
842
|
+
.argument('<tag>', 'Action tag ("product:database:action")')
|
|
843
|
+
.option('--json', 'JSON output')
|
|
844
|
+
.action(wrap((tag, opts) => runDbActions('delete', { tag, json: opts.json }, [])));
|
|
764
845
|
const dbSchema = db.command('schema').description('Schema management for ductape/database/schema.json');
|
|
765
846
|
dbSchema
|
|
766
847
|
.command('generate')
|
|
@@ -24,6 +24,11 @@ export declare function createApp(ctx: WorkspaceContext, body: Record<string, un
|
|
|
24
24
|
export declare function updateApp(ctx: WorkspaceContext, appId: string, body: Record<string, unknown>): Promise<unknown>;
|
|
25
25
|
export declare function updateAppViaProxy(ctx: WorkspaceContext, tag: string, body: Record<string, unknown>): Promise<unknown>;
|
|
26
26
|
export declare function deleteApp(ctx: WorkspaceContext, appId: string): Promise<unknown>;
|
|
27
|
+
export declare function listAppActions(ctx: WorkspaceContext, appTag: string): Promise<unknown>;
|
|
28
|
+
export declare function getAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string): Promise<unknown>;
|
|
29
|
+
export declare function createAppAction(ctx: WorkspaceContext, appTag: string, body: Record<string, unknown>): Promise<unknown>;
|
|
30
|
+
export declare function updateAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string, body: Record<string, unknown>): Promise<unknown>;
|
|
31
|
+
export declare function deleteAppAction(ctx: WorkspaceContext, appTag: string, actionTag: string): Promise<unknown>;
|
|
27
32
|
export interface MarketplaceSearchOptions {
|
|
28
33
|
query?: string;
|
|
29
34
|
category?: string;
|
package/dist/lib/platform-api.js
CHANGED
|
@@ -333,6 +333,26 @@ export async function deleteApp(ctx, appId) {
|
|
|
333
333
|
};
|
|
334
334
|
return client(ctx).delete(`/apps/v1/${appId}?${new URLSearchParams(q).toString()}`);
|
|
335
335
|
}
|
|
336
|
+
// —— App actions (sdk-proxy — admin op, requires access key) ——
|
|
337
|
+
//
|
|
338
|
+
// resource is a path RELATIVE to each of the app's environment base_urls
|
|
339
|
+
// (e.g. "/v1/users/{id}", not a full URL) — Ductape joins base_url + resource
|
|
340
|
+
// per environment at call time.
|
|
341
|
+
export async function listAppActions(ctx, appTag) {
|
|
342
|
+
return unwrapList(await proxy(ctx).execute('actions', 'list', [appTag]));
|
|
343
|
+
}
|
|
344
|
+
export async function getAppAction(ctx, appTag, actionTag) {
|
|
345
|
+
return unwrapOne(await proxy(ctx).execute('actions', 'fetch', [appTag, actionTag]));
|
|
346
|
+
}
|
|
347
|
+
export async function createAppAction(ctx, appTag, body) {
|
|
348
|
+
return unwrapOne(await proxy(ctx).execute('actions', 'create', [appTag, body]));
|
|
349
|
+
}
|
|
350
|
+
export async function updateAppAction(ctx, appTag, actionTag, body) {
|
|
351
|
+
return unwrapOne(await proxy(ctx).execute('actions', 'update', [appTag, actionTag, body]));
|
|
352
|
+
}
|
|
353
|
+
export async function deleteAppAction(ctx, appTag, actionTag) {
|
|
354
|
+
return proxy(ctx).execute('actions', 'delete', [appTag, actionTag]);
|
|
355
|
+
}
|
|
336
356
|
function searchableMarketplaceText(app) {
|
|
337
357
|
if (!app || typeof app !== 'object')
|
|
338
358
|
return '';
|
package/package.json
CHANGED