@amerged/ohmyhost-cli 0.1.44 → 0.1.46
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/README.md +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-VJWPDFZ5.js → chunk-A4ZYW4XW.js} +870 -62
- package/dist/index.js +3 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +1 -1
- package/src/apps/product-cli/src/cli.ts +137 -2
- package/src/apps/product-cli/src/command.ts +186 -2
- package/src/apps/product-cli/src/product-api.ts +202 -2
- package/src/packages/contracts/package.json +5 -0
- package/src/packages/contracts/src/database-events.ts +208 -11
- package/src/packages/contracts/src/database-migration-admission.ts +22 -0
- package/src/packages/contracts/src/database-migration-capture.ts +177 -0
- package/src/packages/contracts/src/database-migration-lexical.ts +1 -0
- package/src/packages/contracts/src/database-tables.ts +317 -0
- package/src/packages/contracts/src/problem-policy.ts +50 -1
- package/src/packages/sdk-ts/package.json +1 -1
- package/src/packages/sdk-ts/src/generated/index.ts +37 -0
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +204 -0
- package/src/packages/sdk-ts/src/generated/types.gen.ts +407 -4
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
StderrDeviceAuthorizationPresenter,
|
|
35
35
|
TokenRefreshRejectedError,
|
|
36
36
|
UserTokenFileError,
|
|
37
|
+
assertDatabaseTableAnswer,
|
|
37
38
|
assertVerifiedLogin,
|
|
38
39
|
chooseLinkedProject,
|
|
39
40
|
chooseWorkspace,
|
|
@@ -100,7 +101,7 @@ import {
|
|
|
100
101
|
selectWorkspace,
|
|
101
102
|
verifiedUserId,
|
|
102
103
|
workspaceOf
|
|
103
|
-
} from "./chunk-
|
|
104
|
+
} from "./chunk-A4ZYW4XW.js";
|
|
104
105
|
import {
|
|
105
106
|
CredentialStoreIntegrityError,
|
|
106
107
|
CredentialStoreUnavailableError,
|
|
@@ -287,6 +288,7 @@ export {
|
|
|
287
288
|
StderrDeviceAuthorizationPresenter,
|
|
288
289
|
TokenRefreshRejectedError,
|
|
289
290
|
UserTokenFileError,
|
|
291
|
+
assertDatabaseTableAnswer,
|
|
290
292
|
assertVerifiedLogin,
|
|
291
293
|
chooseLinkedProject,
|
|
292
294
|
chooseWorkspace,
|
package/package.json
CHANGED
|
@@ -68,7 +68,15 @@ import {
|
|
|
68
68
|
type CurrentCredential,
|
|
69
69
|
type StoredCredential,
|
|
70
70
|
} from "./credential-store.js";
|
|
71
|
-
import type {
|
|
71
|
+
import type {
|
|
72
|
+
DatabaseTableColumnInput,
|
|
73
|
+
DatabaseTableCreateInput,
|
|
74
|
+
ProductApi,
|
|
75
|
+
} from "./product-api.js";
|
|
76
|
+
import {
|
|
77
|
+
parseDatabaseTableCreateRequest,
|
|
78
|
+
type DatabaseTableColumn,
|
|
79
|
+
} from "@ohmyhost/contracts/database-tables";
|
|
72
80
|
import {
|
|
73
81
|
defaultProfileName,
|
|
74
82
|
isProfileUserId,
|
|
@@ -419,8 +427,16 @@ const HELP_USAGE = Object.freeze({
|
|
|
419
427
|
"ohmyhost database access list --project ULID [--environment dev|prod] --json",
|
|
420
428
|
"database access revoke":
|
|
421
429
|
"ohmyhost database access revoke --project ULID --access ULID --yes --json",
|
|
430
|
+
"database watch create":
|
|
431
|
+
"ohmyhost database watch create --project ULID --environment dev|prod --table NAME [--schema public] --columns COL[,COL] [--operations insert,update,delete] --idempotency-key KEY --yes --json (publishes only the listed columns, at most 32, of each row change as the event type data.<topic> printed in this answer; the table must exist as an ordinary table of the application; a sleeping database wakes; next, subscribe with 'ohmyhost database webhook create ... --event-types data.<topic>')",
|
|
432
|
+
"database watch list":
|
|
433
|
+
"ohmyhost database watch list --project ULID [--environment dev|prod] --json (dev when omitted; a sleeping database wakes)",
|
|
434
|
+
"database watch delete":
|
|
435
|
+
"ohmyhost database watch delete --project ULID --environment dev|prod --watch ID --idempotency-key KEY --yes --json",
|
|
436
|
+
"database table create":
|
|
437
|
+
"ohmyhost database table create --project ULID --environment dev|prod --table NAME --columns-file PATH [--primary-key COL[,COL]] --idempotency-key KEY --yes --json (the table is created in the public schema as the migration role, with the same access as migration tables, so a database watch can attach later; the columns file is a UTF-8 JSON array of 1 to 64 column objects, at most 64 KiB, each with name, type and optional nullable, identity and default; types: text, integer, bigint, boolean, numeric, double precision, timestamptz, date, uuid, jsonb; identity needs integer or bigint; default now needs timestamptz and default uuid needs uuid; identical repeats answer created false; at most 100 tables, 64 columns per table)",
|
|
422
438
|
"database webhook create":
|
|
423
|
-
"ohmyhost database webhook create --project ULID --environment dev|prod --url HTTPS_URL --event-types TYPE[,TYPE] [--headers-file PATH] --idempotency-key KEY --yes --json (the signing secret is shown once in this answer; types: database.created, database.ready, database.woke, database.slept, database.suspended, database.resized, database.deleted; the headers file is a UTF-8 JSON object of at most 8 KiB)",
|
|
439
|
+
"ohmyhost database webhook create --project ULID --environment dev|prod --url HTTPS_URL --event-types TYPE[,TYPE] [--headers-file PATH] --idempotency-key KEY --yes --json (the signing secret is shown once in this answer; types: database.created, database.ready, database.woke, database.slept, database.suspended, database.resized, database.deleted, data.<topic> for the rows of one watched table printed by database watch create, or data.* for every watched table; a bare * is not accepted and does not include data events; the headers file is a UTF-8 JSON object of at most 8 KiB)",
|
|
424
440
|
"database webhook list":
|
|
425
441
|
"ohmyhost database webhook list --project ULID [--environment dev|prod] --json (dev when omitted; the secret is never listed)",
|
|
426
442
|
"database webhook get":
|
|
@@ -659,6 +675,9 @@ const SCHEDULE_STATEMENTS_FILE_INVALID_ACTION =
|
|
|
659
675
|
const SCHEDULE_INVALID_ACTION =
|
|
660
676
|
"Check the schedule request: --cron takes five fields in UTC (9 to 120 characters), or --every-seconds takes 60 to 2678400, never both; --timeout-ms takes 1000 to 30000; the statements file must hold 1 to 20 non-blank SQL strings of at most 16 KiB each and 64 KiB in total. Nothing was saved; fix it and run the command again.";
|
|
661
677
|
|
|
678
|
+
const COLUMNS_FILE_INVALID_ACTION =
|
|
679
|
+
"Put one JSON array of 1 to 64 column objects in the file named by --columns-file: UTF-8, at most 64 KiB, each with a lower-case name, a type (text, integer, bigint, boolean, numeric, double precision, timestamptz, date, uuid or jsonb) and optional nullable, identity (integer or bigint only) and default (now for timestamptz, uuid for uuid). Each --primary-key column must be one of them, at most 8 names. Nothing was created; fix the file or the flags and run the command again.";
|
|
680
|
+
|
|
662
681
|
const STATEMENT_FILE_INVALID_ACTION =
|
|
663
682
|
"Put one INSERT, UPDATE or DELETE statement that starts with that keyword (no leading comment or WITH), UTF-8, at most 64 KiB, in the file named by --statement-file, then rerun with the same --idempotency-key.";
|
|
664
683
|
|
|
@@ -1191,6 +1210,14 @@ export class ProductCli {
|
|
|
1191
1210
|
return await this.listDatabaseAccess(command, signal);
|
|
1192
1211
|
if (command.kind === "database-access-revoke")
|
|
1193
1212
|
return await this.revokeDatabaseAccess(command, signal);
|
|
1213
|
+
if (command.kind === "database-watch-create")
|
|
1214
|
+
return await this.createDatabaseWatch(command, signal);
|
|
1215
|
+
if (command.kind === "database-watch-list")
|
|
1216
|
+
return await this.listDatabaseWatches(command, signal);
|
|
1217
|
+
if (command.kind === "database-watch-delete")
|
|
1218
|
+
return await this.deleteDatabaseWatch(command, signal);
|
|
1219
|
+
if (command.kind === "database-table-create")
|
|
1220
|
+
return await this.createDatabaseTable(command, signal);
|
|
1194
1221
|
if (command.kind === "database-webhook-create")
|
|
1195
1222
|
return await this.createDatabaseWebhook(command, signal);
|
|
1196
1223
|
if (command.kind === "database-webhook-list")
|
|
@@ -1952,6 +1979,67 @@ export class ProductCli {
|
|
|
1952
1979
|
return success("database access revoke", { status: "succeeded", access });
|
|
1953
1980
|
}
|
|
1954
1981
|
|
|
1982
|
+
/** The answer names the event type to subscribe to; the watch already carries the columns and the topic. */
|
|
1983
|
+
private async createDatabaseWatch(
|
|
1984
|
+
command: Extract<ProductCliCommand, { kind: "database-watch-create" }>,
|
|
1985
|
+
signal: AbortSignal,
|
|
1986
|
+
): Promise<CliResult> {
|
|
1987
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
1988
|
+
const watch = await api.createProjectDatabaseWatch({
|
|
1989
|
+
projectId: command.projectId,
|
|
1990
|
+
environment: command.environment,
|
|
1991
|
+
idempotencyKey: command.idempotencyKey,
|
|
1992
|
+
request: command.request,
|
|
1993
|
+
});
|
|
1994
|
+
return success("database watch create", {
|
|
1995
|
+
status: "succeeded",
|
|
1996
|
+
watch,
|
|
1997
|
+
next_action: `Run '${this.dependencies.commandPrefix} database webhook create --project ${command.projectId} --environment ${command.environment} --url HTTPS_URL --event-types ${watch.event_type} --idempotency-key KEY --yes --json' to receive ${watch.event_type} events at an HTTPS receiver.`,
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
private async listDatabaseWatches(
|
|
2002
|
+
command: Extract<ProductCliCommand, { kind: "database-watch-list" }>,
|
|
2003
|
+
signal: AbortSignal,
|
|
2004
|
+
): Promise<CliResult> {
|
|
2005
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
2006
|
+
const page = await api.listProjectDatabaseWatches({
|
|
2007
|
+
projectId: command.projectId,
|
|
2008
|
+
environment: command.environment,
|
|
2009
|
+
});
|
|
2010
|
+
return success("database watch list", { status: "succeeded", watch: page.items });
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
private async deleteDatabaseWatch(
|
|
2014
|
+
command: Extract<ProductCliCommand, { kind: "database-watch-delete" }>,
|
|
2015
|
+
signal: AbortSignal,
|
|
2016
|
+
): Promise<CliResult> {
|
|
2017
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
2018
|
+
const deleted = await api.deleteProjectDatabaseWatch({
|
|
2019
|
+
projectId: command.projectId,
|
|
2020
|
+
environment: command.environment,
|
|
2021
|
+
idempotencyKey: command.idempotencyKey,
|
|
2022
|
+
watchId: command.watchId,
|
|
2023
|
+
});
|
|
2024
|
+
return success("database watch delete", { status: "succeeded", deleted });
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/** The columns file and the key are checked before any request; a repeat answers the same table with created false. */
|
|
2028
|
+
private async createDatabaseTable(
|
|
2029
|
+
command: Extract<ProductCliCommand, { kind: "database-table-create" }>,
|
|
2030
|
+
signal: AbortSignal,
|
|
2031
|
+
): Promise<CliResult> {
|
|
2032
|
+
const request = await tableCreateRequest(command);
|
|
2033
|
+
const api = await this.authenticatedApi(command.credentialStore, signal);
|
|
2034
|
+
const answer = await api.createProjectDatabaseTable({
|
|
2035
|
+
projectId: command.projectId,
|
|
2036
|
+
environment: command.environment,
|
|
2037
|
+
idempotencyKey: command.idempotencyKey,
|
|
2038
|
+
request,
|
|
2039
|
+
});
|
|
2040
|
+
return success("database table create", { status: "succeeded", table: answer.table });
|
|
2041
|
+
}
|
|
2042
|
+
|
|
1955
2043
|
/** The signing secret is returned by create only; every other answer carries no secret. */
|
|
1956
2044
|
private async createDatabaseWebhook(
|
|
1957
2045
|
command: Extract<ProductCliCommand, { kind: "database-webhook-create" }>,
|
|
@@ -3422,6 +3510,49 @@ const scheduleSetRequest = async (
|
|
|
3422
3510
|
}
|
|
3423
3511
|
};
|
|
3424
3512
|
|
|
3513
|
+
/**
|
|
3514
|
+
* The table of `database table create`: the columns file is a UTF-8 JSON array, and the table, the
|
|
3515
|
+
* columns and the key are checked by the contract the service uses. A refused file sends nothing.
|
|
3516
|
+
*/
|
|
3517
|
+
const tableCreateRequest = async (
|
|
3518
|
+
command: Extract<ProductCliCommand, { kind: "database-table-create" }>,
|
|
3519
|
+
): Promise<DatabaseTableCreateInput> => {
|
|
3520
|
+
const columnsFailure = () =>
|
|
3521
|
+
new ProductCliFailure(2, "columns_file_invalid", false, COLUMNS_FILE_INVALID_ACTION);
|
|
3522
|
+
const columns = await readBoundedUtf8File(command.columnsFile, 65_536, columnsFailure).then(
|
|
3523
|
+
(text: string): unknown => {
|
|
3524
|
+
try {
|
|
3525
|
+
return JSON.parse(text) as unknown;
|
|
3526
|
+
} catch {
|
|
3527
|
+
throw columnsFailure();
|
|
3528
|
+
}
|
|
3529
|
+
},
|
|
3530
|
+
);
|
|
3531
|
+
try {
|
|
3532
|
+
const parsed = parseDatabaseTableCreateRequest({
|
|
3533
|
+
table: command.table,
|
|
3534
|
+
columns,
|
|
3535
|
+
...(command.primaryKey.length === 0 ? {} : { primaryKey: command.primaryKey }),
|
|
3536
|
+
});
|
|
3537
|
+
return {
|
|
3538
|
+
table: parsed.table,
|
|
3539
|
+
columns: parsed.columns.map(tableColumnInput),
|
|
3540
|
+
primaryKey: parsed.primaryKey,
|
|
3541
|
+
};
|
|
3542
|
+
} catch {
|
|
3543
|
+
throw columnsFailure();
|
|
3544
|
+
}
|
|
3545
|
+
};
|
|
3546
|
+
|
|
3547
|
+
/** The checked column as the REST body names it; a default that is not set is left out, not sent as null. */
|
|
3548
|
+
const tableColumnInput = (column: DatabaseTableColumn): DatabaseTableColumnInput => ({
|
|
3549
|
+
name: column.name,
|
|
3550
|
+
type: column.type,
|
|
3551
|
+
nullable: column.nullable,
|
|
3552
|
+
identity: column.identity,
|
|
3553
|
+
...(column.default === null ? {} : { default: column.default }),
|
|
3554
|
+
});
|
|
3555
|
+
|
|
3425
3556
|
const psqlUnavailable = (): CliResult =>
|
|
3426
3557
|
failure(
|
|
3427
3558
|
"database psql",
|
|
@@ -3526,6 +3657,10 @@ const parsedCommandName = (command: ProductCliCommand): CommandLabel => {
|
|
|
3526
3657
|
if (command.kind === "database-access-list") return "database access list";
|
|
3527
3658
|
if (command.kind === "database-access-revoke") return "database access revoke";
|
|
3528
3659
|
if (command.kind === "database-psql") return "database psql";
|
|
3660
|
+
if (command.kind === "database-watch-create") return "database watch create";
|
|
3661
|
+
if (command.kind === "database-watch-list") return "database watch list";
|
|
3662
|
+
if (command.kind === "database-watch-delete") return "database watch delete";
|
|
3663
|
+
if (command.kind === "database-table-create") return "database table create";
|
|
3529
3664
|
if (command.kind === "database-webhook-create") return "database webhook create";
|
|
3530
3665
|
if (command.kind === "database-webhook-list") return "database webhook list";
|
|
3531
3666
|
if (command.kind === "database-webhook-get") return "database webhook get";
|
|
@@ -6,10 +6,17 @@ import {
|
|
|
6
6
|
parseDatabaseListLimit,
|
|
7
7
|
parseDatabaseScheduleName,
|
|
8
8
|
parseDatabaseUsageWindow,
|
|
9
|
+
parseDatabaseWatchCreateRequest,
|
|
10
|
+
parseDatabaseWatchId,
|
|
9
11
|
parseDatabaseWebhookCreateRequest,
|
|
10
12
|
parseDatabaseWebhookId,
|
|
11
|
-
type
|
|
13
|
+
type DatabaseWatchRequest,
|
|
14
|
+
type DatabaseWebhookEventPattern,
|
|
12
15
|
} from "@ohmyhost/contracts/database-events";
|
|
16
|
+
import {
|
|
17
|
+
DATABASE_TABLE_MAX_PRIMARY_KEY_COLUMNS,
|
|
18
|
+
isDatabaseTableIdentifier,
|
|
19
|
+
} from "@ohmyhost/contracts/database-tables";
|
|
13
20
|
import {
|
|
14
21
|
databaseSizeForMemory,
|
|
15
22
|
type DatabaseComputeSelection,
|
|
@@ -404,12 +411,47 @@ export type ProductCliCommand =
|
|
|
404
411
|
readonly yes: true;
|
|
405
412
|
readonly credentialStore: "native";
|
|
406
413
|
}
|
|
414
|
+
| {
|
|
415
|
+
readonly kind: "database-watch-create";
|
|
416
|
+
readonly projectId: string;
|
|
417
|
+
readonly environment: "dev" | "prod";
|
|
418
|
+
readonly request: DatabaseWatchRequest;
|
|
419
|
+
readonly idempotencyKey: string;
|
|
420
|
+
readonly yes: true;
|
|
421
|
+
readonly credentialStore: "native";
|
|
422
|
+
}
|
|
423
|
+
| {
|
|
424
|
+
readonly kind: "database-table-create";
|
|
425
|
+
readonly projectId: string;
|
|
426
|
+
readonly environment: "dev" | "prod";
|
|
427
|
+
readonly table: string;
|
|
428
|
+
readonly columnsFile: string;
|
|
429
|
+
readonly primaryKey: readonly string[];
|
|
430
|
+
readonly idempotencyKey: string;
|
|
431
|
+
readonly yes: true;
|
|
432
|
+
readonly credentialStore: "native";
|
|
433
|
+
}
|
|
434
|
+
| {
|
|
435
|
+
readonly kind: "database-watch-list";
|
|
436
|
+
readonly projectId: string;
|
|
437
|
+
readonly environment: "dev" | "prod";
|
|
438
|
+
readonly credentialStore: "native";
|
|
439
|
+
}
|
|
440
|
+
| {
|
|
441
|
+
readonly kind: "database-watch-delete";
|
|
442
|
+
readonly projectId: string;
|
|
443
|
+
readonly environment: "dev" | "prod";
|
|
444
|
+
readonly watchId: string;
|
|
445
|
+
readonly idempotencyKey: string;
|
|
446
|
+
readonly yes: true;
|
|
447
|
+
readonly credentialStore: "native";
|
|
448
|
+
}
|
|
407
449
|
| {
|
|
408
450
|
readonly kind: "database-webhook-create";
|
|
409
451
|
readonly projectId: string;
|
|
410
452
|
readonly environment: "dev" | "prod";
|
|
411
453
|
readonly url: string;
|
|
412
|
-
readonly eventTypes: readonly
|
|
454
|
+
readonly eventTypes: readonly DatabaseWebhookEventPattern[];
|
|
413
455
|
readonly headersFile: string | null;
|
|
414
456
|
readonly idempotencyKey: string;
|
|
415
457
|
readonly yes: true;
|
|
@@ -1003,6 +1045,10 @@ export type RecognizedCommandLabel =
|
|
|
1003
1045
|
| "database access create"
|
|
1004
1046
|
| "database access list"
|
|
1005
1047
|
| "database access revoke"
|
|
1048
|
+
| "database watch create"
|
|
1049
|
+
| "database watch list"
|
|
1050
|
+
| "database watch delete"
|
|
1051
|
+
| "database table create"
|
|
1006
1052
|
| "database webhook create"
|
|
1007
1053
|
| "database webhook list"
|
|
1008
1054
|
| "database webhook get"
|
|
@@ -1174,6 +1220,13 @@ export const recognizedCommandLabel = (argv: readonly string[]): RecognizedComma
|
|
|
1174
1220
|
if (values[1] === "list") return "database access list";
|
|
1175
1221
|
if (values[1] === "revoke") return "database access revoke";
|
|
1176
1222
|
}
|
|
1223
|
+
if (value === "database" && values[0] === "watch") {
|
|
1224
|
+
if (values[1] === "create") return "database watch create";
|
|
1225
|
+
if (values[1] === "list") return "database watch list";
|
|
1226
|
+
if (values[1] === "delete") return "database watch delete";
|
|
1227
|
+
}
|
|
1228
|
+
if (value === "database" && values[0] === "table" && values[1] === "create")
|
|
1229
|
+
return "database table create";
|
|
1177
1230
|
if (value === "database" && values[0] === "webhook") {
|
|
1178
1231
|
if (values[1] === "create") return "database webhook create";
|
|
1179
1232
|
if (values[1] === "list") return "database webhook list";
|
|
@@ -2063,6 +2116,137 @@ export const parseProductCliCommand = (argv: readonly string[]): ProductCliComma
|
|
|
2063
2116
|
}
|
|
2064
2117
|
throw new InvalidCommandError();
|
|
2065
2118
|
}
|
|
2119
|
+
if (first === "database" && values[0] === "watch") {
|
|
2120
|
+
values.shift();
|
|
2121
|
+
const subcommand = values.shift();
|
|
2122
|
+
if (subcommand === "create") {
|
|
2123
|
+
if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
|
|
2124
|
+
const options = parseOptionalOptions(values, [
|
|
2125
|
+
"--project",
|
|
2126
|
+
"--environment",
|
|
2127
|
+
"--table",
|
|
2128
|
+
"--schema",
|
|
2129
|
+
"--columns",
|
|
2130
|
+
"--operations",
|
|
2131
|
+
"--idempotency-key",
|
|
2132
|
+
]);
|
|
2133
|
+
const projectId = options["--project"],
|
|
2134
|
+
environment = options["--environment"],
|
|
2135
|
+
table = options["--table"],
|
|
2136
|
+
schema = options["--schema"],
|
|
2137
|
+
columns = options["--columns"],
|
|
2138
|
+
operations = options["--operations"],
|
|
2139
|
+
idempotencyKey = options["--idempotency-key"];
|
|
2140
|
+
if (
|
|
2141
|
+
!isValue(projectId) ||
|
|
2142
|
+
!isValue(idempotencyKey) ||
|
|
2143
|
+
!isValue(table) ||
|
|
2144
|
+
!isValue(columns) ||
|
|
2145
|
+
(environment !== "dev" && environment !== "prod")
|
|
2146
|
+
)
|
|
2147
|
+
throw new InvalidCommandError();
|
|
2148
|
+
const request = contractValue(() =>
|
|
2149
|
+
parseDatabaseWatchCreateRequest({
|
|
2150
|
+
schema,
|
|
2151
|
+
table,
|
|
2152
|
+
columns: columns.split(","),
|
|
2153
|
+
...(operations === undefined ? {} : { operations: operations.split(",") }),
|
|
2154
|
+
}),
|
|
2155
|
+
);
|
|
2156
|
+
return {
|
|
2157
|
+
kind: "database-watch-create",
|
|
2158
|
+
projectId,
|
|
2159
|
+
environment,
|
|
2160
|
+
request,
|
|
2161
|
+
idempotencyKey,
|
|
2162
|
+
yes: true,
|
|
2163
|
+
credentialStore,
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
if (subcommand === "list") {
|
|
2167
|
+
if (yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
|
|
2168
|
+
const options = parseOptionalOptions(values, ["--project", "--environment"]);
|
|
2169
|
+
const projectId = options["--project"],
|
|
2170
|
+
environment = options["--environment"] ?? "dev";
|
|
2171
|
+
if (!isValue(projectId) || (environment !== "dev" && environment !== "prod"))
|
|
2172
|
+
throw new InvalidCommandError();
|
|
2173
|
+
return { kind: "database-watch-list", projectId, environment, credentialStore };
|
|
2174
|
+
}
|
|
2175
|
+
if (subcommand === "delete") {
|
|
2176
|
+
if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
|
|
2177
|
+
const options = parseOptionalOptions(values, [
|
|
2178
|
+
"--project",
|
|
2179
|
+
"--environment",
|
|
2180
|
+
"--watch",
|
|
2181
|
+
"--idempotency-key",
|
|
2182
|
+
]);
|
|
2183
|
+
const projectId = options["--project"],
|
|
2184
|
+
environment = options["--environment"],
|
|
2185
|
+
watchId = options["--watch"],
|
|
2186
|
+
idempotencyKey = options["--idempotency-key"];
|
|
2187
|
+
if (
|
|
2188
|
+
!isValue(projectId) ||
|
|
2189
|
+
!isValue(idempotencyKey) ||
|
|
2190
|
+
(environment !== "dev" && environment !== "prod")
|
|
2191
|
+
)
|
|
2192
|
+
throw new InvalidCommandError();
|
|
2193
|
+
return {
|
|
2194
|
+
kind: "database-watch-delete",
|
|
2195
|
+
projectId,
|
|
2196
|
+
environment,
|
|
2197
|
+
watchId: contractValue(() => parseDatabaseWatchId(watchId)),
|
|
2198
|
+
idempotencyKey,
|
|
2199
|
+
yes: true,
|
|
2200
|
+
credentialStore,
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
throw new InvalidCommandError();
|
|
2204
|
+
}
|
|
2205
|
+
if (first === "database" && values[0] === "table") {
|
|
2206
|
+
values.shift();
|
|
2207
|
+
const subcommand = values.shift();
|
|
2208
|
+
if (subcommand === "create") {
|
|
2209
|
+
if (!yes || wait || follow || revoke || stdin) throw new InvalidCommandError();
|
|
2210
|
+
const options = parseOptionalOptions(values, [
|
|
2211
|
+
"--project",
|
|
2212
|
+
"--environment",
|
|
2213
|
+
"--table",
|
|
2214
|
+
"--columns-file",
|
|
2215
|
+
"--primary-key",
|
|
2216
|
+
"--idempotency-key",
|
|
2217
|
+
]);
|
|
2218
|
+
const projectId = options["--project"],
|
|
2219
|
+
environment = options["--environment"],
|
|
2220
|
+
table = options["--table"],
|
|
2221
|
+
columnsFile = options["--columns-file"],
|
|
2222
|
+
primaryKey = options["--primary-key"],
|
|
2223
|
+
idempotencyKey = options["--idempotency-key"];
|
|
2224
|
+
const keyNames = primaryKey === undefined ? [] : primaryKey.split(",");
|
|
2225
|
+
if (
|
|
2226
|
+
!isValue(projectId) ||
|
|
2227
|
+
!isValue(idempotencyKey) ||
|
|
2228
|
+
!isValue(table) ||
|
|
2229
|
+
!isValue(columnsFile) ||
|
|
2230
|
+
(primaryKey !== undefined && !keyNames.every(isDatabaseTableIdentifier)) ||
|
|
2231
|
+
keyNames.length > DATABASE_TABLE_MAX_PRIMARY_KEY_COLUMNS ||
|
|
2232
|
+
!isDatabaseTableIdentifier(table) ||
|
|
2233
|
+
(environment !== "dev" && environment !== "prod")
|
|
2234
|
+
)
|
|
2235
|
+
throw new InvalidCommandError();
|
|
2236
|
+
return {
|
|
2237
|
+
kind: "database-table-create",
|
|
2238
|
+
projectId,
|
|
2239
|
+
environment,
|
|
2240
|
+
table,
|
|
2241
|
+
columnsFile,
|
|
2242
|
+
primaryKey: keyNames,
|
|
2243
|
+
idempotencyKey,
|
|
2244
|
+
yes: true,
|
|
2245
|
+
credentialStore,
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
throw new InvalidCommandError();
|
|
2249
|
+
}
|
|
2066
2250
|
if (first === "database" && values[0] === "webhook") {
|
|
2067
2251
|
values.shift();
|
|
2068
2252
|
const subcommand = values.shift();
|