@amerged/ohmyhost-cli 0.1.40 → 0.1.42
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-MRBUGDRW.js → chunk-HBM2X2MV.js} +1626 -259
- package/dist/index.js +5 -3
- package/dist/licenses/pgcf-bridge-NOTICE +1 -1
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +1 -1
- package/src/apps/product-cli/src/cli.ts +440 -40
- package/src/apps/product-cli/src/command.ts +492 -19
- package/src/apps/product-cli/src/composition.ts +6 -3
- package/src/apps/product-cli/src/product-api.ts +366 -13
- package/src/apps/product-cli/src/runtime-contract.ts +1 -10
- package/src/packages/contracts/package.json +5 -0
- package/src/packages/contracts/src/credit-pricing.ts +78 -50
- package/src/packages/contracts/src/database-access.ts +24 -45
- package/src/packages/contracts/src/database-events.ts +697 -0
- package/src/packages/contracts/src/feedback.ts +0 -5
- package/src/packages/contracts/src/problem-policy.ts +28 -0
- package/src/packages/postgres-bridge/NOTICE +1 -1
- package/src/packages/postgres-bridge/src/bridge.ts +0 -4
- package/src/packages/sdk-ts/package.json +1 -1
- package/src/packages/sdk-ts/src/generated/index.ts +109 -0
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +702 -10
- package/src/packages/sdk-ts/src/generated/types.gen.ts +1205 -49
|
@@ -20,6 +20,36 @@ import {
|
|
|
20
20
|
type ProjectDatabaseWriteReceipt,
|
|
21
21
|
} from "@ohmyhost/contracts/database-access";
|
|
22
22
|
import { assertDatabaseCompute, type DatabaseCompute } from "@ohmyhost/contracts/database-compute";
|
|
23
|
+
import {
|
|
24
|
+
assertDatabaseExecutionUsagePage,
|
|
25
|
+
assertDatabaseSchedule,
|
|
26
|
+
assertDatabaseSchedulePage,
|
|
27
|
+
assertDatabaseScheduleDeleted,
|
|
28
|
+
assertDatabaseScheduleRunAccepted,
|
|
29
|
+
assertDatabaseScheduleRunPage,
|
|
30
|
+
assertDatabaseWebhook,
|
|
31
|
+
assertDatabaseWebhookCreated,
|
|
32
|
+
assertDatabaseWebhookDeleted,
|
|
33
|
+
assertDatabaseWebhookDeliveryPage,
|
|
34
|
+
assertDatabaseWebhookPage,
|
|
35
|
+
assertDatabaseWebhookReplayAccepted,
|
|
36
|
+
assertDatabaseWebhookTest,
|
|
37
|
+
type DatabaseExecutionUsagePage,
|
|
38
|
+
type DatabaseSchedule,
|
|
39
|
+
type DatabaseScheduleDeleted,
|
|
40
|
+
type DatabaseSchedulePage,
|
|
41
|
+
type DatabaseScheduleRunAccepted,
|
|
42
|
+
type DatabaseScheduleRunPage,
|
|
43
|
+
type DatabaseScheduleSetRequest,
|
|
44
|
+
type DatabaseWebhook,
|
|
45
|
+
type DatabaseWebhookCreated,
|
|
46
|
+
type DatabaseWebhookDeleted,
|
|
47
|
+
type DatabaseWebhookDeliveryPage,
|
|
48
|
+
type DatabaseWebhookEventType,
|
|
49
|
+
type DatabaseWebhookPage,
|
|
50
|
+
type DatabaseWebhookReplayAccepted,
|
|
51
|
+
type DatabaseWebhookTestResult,
|
|
52
|
+
} from "@ohmyhost/contracts/database-events";
|
|
23
53
|
import type { DatabaseComputeSelection } from "@ohmyhost/contracts/database-sizes";
|
|
24
54
|
import {
|
|
25
55
|
assertProjectContext,
|
|
@@ -117,6 +147,20 @@ import {
|
|
|
117
147
|
createProjectDatabaseAccess,
|
|
118
148
|
listProjectDatabaseAccess,
|
|
119
149
|
revokeProjectDatabaseAccess,
|
|
150
|
+
createProjectDatabaseWebhook,
|
|
151
|
+
listProjectDatabaseWebhooks,
|
|
152
|
+
getProjectDatabaseWebhook,
|
|
153
|
+
deleteProjectDatabaseWebhook,
|
|
154
|
+
testProjectDatabaseWebhook,
|
|
155
|
+
listProjectDatabaseWebhookDeliveries,
|
|
156
|
+
replayProjectDatabaseWebhookDelivery,
|
|
157
|
+
setProjectDatabaseSchedule,
|
|
158
|
+
listProjectDatabaseSchedules,
|
|
159
|
+
getProjectDatabaseSchedule,
|
|
160
|
+
deleteProjectDatabaseSchedule,
|
|
161
|
+
listProjectDatabaseScheduleRuns,
|
|
162
|
+
runProjectDatabaseSchedule,
|
|
163
|
+
getProjectDatabaseExecutionUsage,
|
|
120
164
|
reconcileOperation,
|
|
121
165
|
streamOperationEvents,
|
|
122
166
|
type DeploymentPlan,
|
|
@@ -230,11 +274,7 @@ export interface ProductApi {
|
|
|
230
274
|
organizationId: string,
|
|
231
275
|
): Promise<import("@ohmyhost/sdk-ts").OrganizationReferral>;
|
|
232
276
|
getOrganizationCredits(organizationId: string): Promise<OrganizationCredits>;
|
|
233
|
-
getOrganizationCreditUsage(
|
|
234
|
-
organizationId: string;
|
|
235
|
-
month: string;
|
|
236
|
-
cursor?: string;
|
|
237
|
-
}): Promise<OrganizationCreditUsage>;
|
|
277
|
+
getOrganizationCreditUsage(organizationId: string): Promise<OrganizationCreditUsage>;
|
|
238
278
|
getProjectCreditBudget(projectId: string): Promise<ProjectCreditBudget>;
|
|
239
279
|
setProjectCreditBudget(input: {
|
|
240
280
|
projectId: string;
|
|
@@ -283,6 +323,85 @@ export interface ProductApi {
|
|
|
283
323
|
readonly projectId: string;
|
|
284
324
|
readonly accessId: string;
|
|
285
325
|
}): Promise<ProjectDatabaseAccess>;
|
|
326
|
+
createProjectDatabaseWebhook(input: {
|
|
327
|
+
readonly projectId: string;
|
|
328
|
+
readonly environment: "dev" | "prod";
|
|
329
|
+
readonly idempotencyKey: string;
|
|
330
|
+
readonly url: string;
|
|
331
|
+
readonly eventTypes: readonly DatabaseWebhookEventType[];
|
|
332
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
333
|
+
}): Promise<DatabaseWebhookCreated>;
|
|
334
|
+
listProjectDatabaseWebhooks(input: {
|
|
335
|
+
readonly projectId: string;
|
|
336
|
+
readonly environment: "dev" | "prod";
|
|
337
|
+
}): Promise<DatabaseWebhookPage>;
|
|
338
|
+
getProjectDatabaseWebhook(input: {
|
|
339
|
+
readonly projectId: string;
|
|
340
|
+
readonly environment: "dev" | "prod";
|
|
341
|
+
readonly webhookId: string;
|
|
342
|
+
}): Promise<DatabaseWebhook>;
|
|
343
|
+
deleteProjectDatabaseWebhook(input: {
|
|
344
|
+
readonly projectId: string;
|
|
345
|
+
readonly environment: "dev" | "prod";
|
|
346
|
+
readonly webhookId: string;
|
|
347
|
+
}): Promise<DatabaseWebhookDeleted>;
|
|
348
|
+
testProjectDatabaseWebhook(input: {
|
|
349
|
+
readonly projectId: string;
|
|
350
|
+
readonly environment: "dev" | "prod";
|
|
351
|
+
readonly webhookId: string;
|
|
352
|
+
}): Promise<DatabaseWebhookTestResult>;
|
|
353
|
+
listProjectDatabaseWebhookDeliveries(input: {
|
|
354
|
+
readonly projectId: string;
|
|
355
|
+
readonly environment: "dev" | "prod";
|
|
356
|
+
readonly webhookId?: string;
|
|
357
|
+
readonly limit?: number;
|
|
358
|
+
readonly cursor?: string;
|
|
359
|
+
}): Promise<DatabaseWebhookDeliveryPage>;
|
|
360
|
+
replayProjectDatabaseWebhookDelivery(input: {
|
|
361
|
+
readonly projectId: string;
|
|
362
|
+
readonly environment: "dev" | "prod";
|
|
363
|
+
readonly deliveryId: string;
|
|
364
|
+
}): Promise<DatabaseWebhookReplayAccepted>;
|
|
365
|
+
setProjectDatabaseSchedule(input: {
|
|
366
|
+
readonly projectId: string;
|
|
367
|
+
readonly environment: "dev" | "prod";
|
|
368
|
+
readonly name: string;
|
|
369
|
+
readonly request: DatabaseScheduleSetRequest;
|
|
370
|
+
}): Promise<DatabaseSchedule>;
|
|
371
|
+
listProjectDatabaseSchedules(input: {
|
|
372
|
+
readonly projectId: string;
|
|
373
|
+
readonly environment: "dev" | "prod";
|
|
374
|
+
}): Promise<DatabaseSchedulePage>;
|
|
375
|
+
getProjectDatabaseSchedule(input: {
|
|
376
|
+
readonly projectId: string;
|
|
377
|
+
readonly environment: "dev" | "prod";
|
|
378
|
+
readonly name: string;
|
|
379
|
+
}): Promise<DatabaseSchedule>;
|
|
380
|
+
deleteProjectDatabaseSchedule(input: {
|
|
381
|
+
readonly projectId: string;
|
|
382
|
+
readonly environment: "dev" | "prod";
|
|
383
|
+
readonly name: string;
|
|
384
|
+
}): Promise<DatabaseScheduleDeleted>;
|
|
385
|
+
listProjectDatabaseScheduleRuns(input: {
|
|
386
|
+
readonly projectId: string;
|
|
387
|
+
readonly environment: "dev" | "prod";
|
|
388
|
+
readonly name: string;
|
|
389
|
+
readonly limit?: number;
|
|
390
|
+
}): Promise<DatabaseScheduleRunPage>;
|
|
391
|
+
runProjectDatabaseSchedule(input: {
|
|
392
|
+
readonly projectId: string;
|
|
393
|
+
readonly environment: "dev" | "prod";
|
|
394
|
+
readonly name: string;
|
|
395
|
+
}): Promise<DatabaseScheduleRunAccepted>;
|
|
396
|
+
getProjectDatabaseExecutionUsage(input: {
|
|
397
|
+
readonly projectId: string;
|
|
398
|
+
readonly environment: "dev" | "prod";
|
|
399
|
+
readonly from: string;
|
|
400
|
+
readonly to: string;
|
|
401
|
+
readonly granularity: "hour" | "day";
|
|
402
|
+
readonly limit?: number;
|
|
403
|
+
readonly cursor?: string;
|
|
404
|
+
}): Promise<DatabaseExecutionUsagePage>;
|
|
286
405
|
planPaidDomain(input: {
|
|
287
406
|
readonly projectId: string;
|
|
288
407
|
readonly hostname: string;
|
|
@@ -445,6 +564,11 @@ export interface ProductApi {
|
|
|
445
564
|
managedMail?(input: ManagedMailCommand): Promise<unknown>;
|
|
446
565
|
}
|
|
447
566
|
|
|
567
|
+
/** A webhook answer that names another environment, webhook or delivery than the request did. */
|
|
568
|
+
const WEBHOOK_SCOPE_MISMATCH = "Webhook response scope mismatch";
|
|
569
|
+
/** A schedule answer that names another environment or schedule than the request did. */
|
|
570
|
+
const SCHEDULE_SCOPE_MISMATCH = "Schedule response scope mismatch";
|
|
571
|
+
|
|
448
572
|
export class GeneratedSdkProductApi implements ProductApi {
|
|
449
573
|
readonly #client: OhMyHostClient;
|
|
450
574
|
public readonly managedSource: ManagedSourceApi;
|
|
@@ -614,15 +738,9 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
614
738
|
{ client: this.#client },
|
|
615
739
|
);
|
|
616
740
|
}
|
|
617
|
-
public getOrganizationCreditUsage(
|
|
618
|
-
input: Parameters<ProductApi["getOrganizationCreditUsage"]>[0],
|
|
619
|
-
) {
|
|
741
|
+
public getOrganizationCreditUsage(organizationId: string) {
|
|
620
742
|
return getOrganizationCreditUsage(
|
|
621
|
-
{
|
|
622
|
-
organization_id: input.organizationId,
|
|
623
|
-
month: input.month,
|
|
624
|
-
...(input.cursor ? { cursor: input.cursor } : {}),
|
|
625
|
-
},
|
|
743
|
+
{ organization_id: organizationId },
|
|
626
744
|
{ client: this.#client },
|
|
627
745
|
);
|
|
628
746
|
}
|
|
@@ -771,6 +889,241 @@ export class GeneratedSdkProductApi implements ProductApi {
|
|
|
771
889
|
return value;
|
|
772
890
|
}
|
|
773
891
|
|
|
892
|
+
/** The signing secret exists only in this answer; the CLI prints it once and stores nothing. */
|
|
893
|
+
public async createProjectDatabaseWebhook(
|
|
894
|
+
input: Parameters<ProductApi["createProjectDatabaseWebhook"]>[0],
|
|
895
|
+
): Promise<DatabaseWebhookCreated> {
|
|
896
|
+
const value = await createProjectDatabaseWebhook(
|
|
897
|
+
{
|
|
898
|
+
project_id: input.projectId,
|
|
899
|
+
environment: input.environment,
|
|
900
|
+
"Idempotency-Key": input.idempotencyKey,
|
|
901
|
+
databaseWebhookCreateRequest: {
|
|
902
|
+
url: input.url,
|
|
903
|
+
event_types: [...input.eventTypes],
|
|
904
|
+
headers: { ...input.headers },
|
|
905
|
+
},
|
|
906
|
+
},
|
|
907
|
+
{ client: this.#client },
|
|
908
|
+
);
|
|
909
|
+
assertDatabaseWebhookCreated(value);
|
|
910
|
+
if (value.environment !== input.environment) throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
911
|
+
return value;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
public async listProjectDatabaseWebhooks(
|
|
915
|
+
input: Parameters<ProductApi["listProjectDatabaseWebhooks"]>[0],
|
|
916
|
+
): Promise<DatabaseWebhookPage> {
|
|
917
|
+
const value = await listProjectDatabaseWebhooks(
|
|
918
|
+
{ project_id: input.projectId, environment: input.environment },
|
|
919
|
+
{ client: this.#client },
|
|
920
|
+
);
|
|
921
|
+
assertDatabaseWebhookPage(value);
|
|
922
|
+
if (value.items.some((item) => item.environment !== input.environment))
|
|
923
|
+
throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
924
|
+
return value;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
public async getProjectDatabaseWebhook(
|
|
928
|
+
input: Parameters<ProductApi["getProjectDatabaseWebhook"]>[0],
|
|
929
|
+
): Promise<DatabaseWebhook> {
|
|
930
|
+
const value = await getProjectDatabaseWebhook(
|
|
931
|
+
{
|
|
932
|
+
project_id: input.projectId,
|
|
933
|
+
environment: input.environment,
|
|
934
|
+
webhook_id: input.webhookId,
|
|
935
|
+
},
|
|
936
|
+
{ client: this.#client },
|
|
937
|
+
);
|
|
938
|
+
assertDatabaseWebhook(value);
|
|
939
|
+
if (value.id !== input.webhookId || value.environment !== input.environment)
|
|
940
|
+
throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
941
|
+
return value;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
public async deleteProjectDatabaseWebhook(
|
|
945
|
+
input: Parameters<ProductApi["deleteProjectDatabaseWebhook"]>[0],
|
|
946
|
+
): Promise<DatabaseWebhookDeleted> {
|
|
947
|
+
const value = await deleteProjectDatabaseWebhook(
|
|
948
|
+
{
|
|
949
|
+
project_id: input.projectId,
|
|
950
|
+
environment: input.environment,
|
|
951
|
+
webhook_id: input.webhookId,
|
|
952
|
+
},
|
|
953
|
+
{ client: this.#client },
|
|
954
|
+
);
|
|
955
|
+
assertDatabaseWebhookDeleted(value);
|
|
956
|
+
if (value.id !== input.webhookId) throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
957
|
+
return value;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
public async testProjectDatabaseWebhook(
|
|
961
|
+
input: Parameters<ProductApi["testProjectDatabaseWebhook"]>[0],
|
|
962
|
+
): Promise<DatabaseWebhookTestResult> {
|
|
963
|
+
const value = await testProjectDatabaseWebhook(
|
|
964
|
+
{
|
|
965
|
+
project_id: input.projectId,
|
|
966
|
+
environment: input.environment,
|
|
967
|
+
webhook_id: input.webhookId,
|
|
968
|
+
},
|
|
969
|
+
{ client: this.#client },
|
|
970
|
+
);
|
|
971
|
+
assertDatabaseWebhookTest(value);
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
public async listProjectDatabaseWebhookDeliveries(
|
|
976
|
+
input: Parameters<ProductApi["listProjectDatabaseWebhookDeliveries"]>[0],
|
|
977
|
+
): Promise<DatabaseWebhookDeliveryPage> {
|
|
978
|
+
const value = await listProjectDatabaseWebhookDeliveries(
|
|
979
|
+
{
|
|
980
|
+
project_id: input.projectId,
|
|
981
|
+
environment: input.environment,
|
|
982
|
+
...(input.webhookId === undefined ? {} : { webhook_id: input.webhookId }),
|
|
983
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
984
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
985
|
+
},
|
|
986
|
+
{ client: this.#client },
|
|
987
|
+
);
|
|
988
|
+
assertDatabaseWebhookDeliveryPage(value);
|
|
989
|
+
if (
|
|
990
|
+
input.webhookId !== undefined &&
|
|
991
|
+
value.items.some((item) => item.webhook_id !== input.webhookId)
|
|
992
|
+
)
|
|
993
|
+
throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
994
|
+
return value;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
public async replayProjectDatabaseWebhookDelivery(
|
|
998
|
+
input: Parameters<ProductApi["replayProjectDatabaseWebhookDelivery"]>[0],
|
|
999
|
+
): Promise<DatabaseWebhookReplayAccepted> {
|
|
1000
|
+
const value = await replayProjectDatabaseWebhookDelivery(
|
|
1001
|
+
{
|
|
1002
|
+
project_id: input.projectId,
|
|
1003
|
+
environment: input.environment,
|
|
1004
|
+
delivery_id: input.deliveryId,
|
|
1005
|
+
},
|
|
1006
|
+
{ client: this.#client },
|
|
1007
|
+
);
|
|
1008
|
+
assertDatabaseWebhookReplayAccepted(value);
|
|
1009
|
+
if (value.delivery_id !== input.deliveryId) throw new TypeError(WEBHOOK_SCOPE_MISMATCH);
|
|
1010
|
+
return value;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/** A schedule runs the statements as the application's runtime role; the request confirms it. */
|
|
1014
|
+
public async setProjectDatabaseSchedule(
|
|
1015
|
+
input: Parameters<ProductApi["setProjectDatabaseSchedule"]>[0],
|
|
1016
|
+
): Promise<DatabaseSchedule> {
|
|
1017
|
+
const request = input.request;
|
|
1018
|
+
const value = await setProjectDatabaseSchedule(
|
|
1019
|
+
{
|
|
1020
|
+
project_id: input.projectId,
|
|
1021
|
+
environment: input.environment,
|
|
1022
|
+
name: input.name,
|
|
1023
|
+
databaseScheduleSetRequest: {
|
|
1024
|
+
...(request.cron === null ? {} : { cron: request.cron }),
|
|
1025
|
+
...(request.every_seconds === null ? {} : { every_seconds: request.every_seconds }),
|
|
1026
|
+
statements: [...request.statements],
|
|
1027
|
+
timeout_ms: request.timeout_ms,
|
|
1028
|
+
enabled: request.enabled,
|
|
1029
|
+
confirm: true,
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
{ client: this.#client },
|
|
1033
|
+
);
|
|
1034
|
+
assertDatabaseSchedule(value);
|
|
1035
|
+
if (value.name !== input.name || value.environment !== input.environment)
|
|
1036
|
+
throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1037
|
+
return value;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
public async listProjectDatabaseSchedules(
|
|
1041
|
+
input: Parameters<ProductApi["listProjectDatabaseSchedules"]>[0],
|
|
1042
|
+
): Promise<DatabaseSchedulePage> {
|
|
1043
|
+
const value = await listProjectDatabaseSchedules(
|
|
1044
|
+
{ project_id: input.projectId, environment: input.environment },
|
|
1045
|
+
{ client: this.#client },
|
|
1046
|
+
);
|
|
1047
|
+
assertDatabaseSchedulePage(value);
|
|
1048
|
+
if (value.items.some((item) => item.environment !== input.environment))
|
|
1049
|
+
throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1050
|
+
return value;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
public async getProjectDatabaseSchedule(
|
|
1054
|
+
input: Parameters<ProductApi["getProjectDatabaseSchedule"]>[0],
|
|
1055
|
+
): Promise<DatabaseSchedule> {
|
|
1056
|
+
const value = await getProjectDatabaseSchedule(
|
|
1057
|
+
{ project_id: input.projectId, environment: input.environment, name: input.name },
|
|
1058
|
+
{ client: this.#client },
|
|
1059
|
+
);
|
|
1060
|
+
assertDatabaseSchedule(value);
|
|
1061
|
+
if (value.name !== input.name || value.environment !== input.environment)
|
|
1062
|
+
throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1063
|
+
return value;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
public async deleteProjectDatabaseSchedule(
|
|
1067
|
+
input: Parameters<ProductApi["deleteProjectDatabaseSchedule"]>[0],
|
|
1068
|
+
): Promise<DatabaseScheduleDeleted> {
|
|
1069
|
+
const value = await deleteProjectDatabaseSchedule(
|
|
1070
|
+
{ project_id: input.projectId, environment: input.environment, name: input.name },
|
|
1071
|
+
{ client: this.#client },
|
|
1072
|
+
);
|
|
1073
|
+
assertDatabaseScheduleDeleted(value);
|
|
1074
|
+
if (value.name !== input.name) throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1075
|
+
return value;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
public async listProjectDatabaseScheduleRuns(
|
|
1079
|
+
input: Parameters<ProductApi["listProjectDatabaseScheduleRuns"]>[0],
|
|
1080
|
+
): Promise<DatabaseScheduleRunPage> {
|
|
1081
|
+
const value = await listProjectDatabaseScheduleRuns(
|
|
1082
|
+
{
|
|
1083
|
+
project_id: input.projectId,
|
|
1084
|
+
environment: input.environment,
|
|
1085
|
+
name: input.name,
|
|
1086
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
1087
|
+
},
|
|
1088
|
+
{ client: this.#client },
|
|
1089
|
+
);
|
|
1090
|
+
assertDatabaseScheduleRunPage(value);
|
|
1091
|
+
if (value.items.some((item) => item.schedule !== input.name))
|
|
1092
|
+
throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1093
|
+
return value;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
public async runProjectDatabaseSchedule(
|
|
1097
|
+
input: Parameters<ProductApi["runProjectDatabaseSchedule"]>[0],
|
|
1098
|
+
): Promise<DatabaseScheduleRunAccepted> {
|
|
1099
|
+
const value = await runProjectDatabaseSchedule(
|
|
1100
|
+
{ project_id: input.projectId, environment: input.environment, name: input.name },
|
|
1101
|
+
{ client: this.#client },
|
|
1102
|
+
);
|
|
1103
|
+
assertDatabaseScheduleRunAccepted(value);
|
|
1104
|
+
if (value.schedule !== input.name) throw new TypeError(SCHEDULE_SCOPE_MISMATCH);
|
|
1105
|
+
return value;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
public async getProjectDatabaseExecutionUsage(
|
|
1109
|
+
input: Parameters<ProductApi["getProjectDatabaseExecutionUsage"]>[0],
|
|
1110
|
+
): Promise<DatabaseExecutionUsagePage> {
|
|
1111
|
+
const value = await getProjectDatabaseExecutionUsage(
|
|
1112
|
+
{
|
|
1113
|
+
project_id: input.projectId,
|
|
1114
|
+
environment: input.environment,
|
|
1115
|
+
from: input.from,
|
|
1116
|
+
to: input.to,
|
|
1117
|
+
granularity: input.granularity,
|
|
1118
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
1119
|
+
...(input.cursor === undefined ? {} : { cursor: input.cursor }),
|
|
1120
|
+
},
|
|
1121
|
+
{ client: this.#client },
|
|
1122
|
+
);
|
|
1123
|
+
assertDatabaseExecutionUsagePage(value);
|
|
1124
|
+
return value;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
774
1127
|
public async planPaidDomain(input: {
|
|
775
1128
|
readonly projectId: string;
|
|
776
1129
|
readonly hostname: string;
|
|
@@ -552,9 +552,6 @@ export function parseOrganizationCredits(value: unknown): OrganizationCredits {
|
|
|
552
552
|
"as_of",
|
|
553
553
|
"available_micros",
|
|
554
554
|
"reserved_micros",
|
|
555
|
-
"spent_micros",
|
|
556
|
-
"expired_micros",
|
|
557
|
-
"platform_overrun_micros",
|
|
558
555
|
"grace_started_at",
|
|
559
556
|
"grace_expires_at",
|
|
560
557
|
"active_meters",
|
|
@@ -586,13 +583,7 @@ export function parseOrganizationCredits(value: unknown): OrganizationCredits {
|
|
|
586
583
|
)
|
|
587
584
|
)
|
|
588
585
|
throw new ResponseContractError();
|
|
589
|
-
for (const field of [
|
|
590
|
-
"available_micros",
|
|
591
|
-
"reserved_micros",
|
|
592
|
-
"spent_micros",
|
|
593
|
-
"expired_micros",
|
|
594
|
-
"platform_overrun_micros",
|
|
595
|
-
]) {
|
|
586
|
+
for (const field of ["available_micros", "reserved_micros"]) {
|
|
596
587
|
const amount = input[field];
|
|
597
588
|
if (
|
|
598
589
|
typeof amount !== "string" ||
|
|
@@ -127,6 +127,11 @@
|
|
|
127
127
|
"types": "./dist/database-access.d.ts",
|
|
128
128
|
"import": "./dist/database-access.js"
|
|
129
129
|
},
|
|
130
|
+
"./database-events": {
|
|
131
|
+
"source": "./src/database-events.ts",
|
|
132
|
+
"types": "./dist/database-events.d.ts",
|
|
133
|
+
"import": "./dist/database-events.js"
|
|
134
|
+
},
|
|
130
135
|
"./functions": {
|
|
131
136
|
"source": "./src/functions.ts",
|
|
132
137
|
"types": "./dist/functions.d.ts",
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
export interface OrganizationCreditUsage {
|
|
2
2
|
organization_id: string;
|
|
3
|
-
month: string;
|
|
4
3
|
as_of: string;
|
|
5
4
|
unit: "microcredits";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
/** The UTC month (`YYYY-MM`) that `projects[].used_micros` counts. */
|
|
6
|
+
period: string;
|
|
7
|
+
/** The first instant of the daily lines: the start of the oldest UTC day that is reported. */
|
|
8
|
+
from: string;
|
|
9
|
+
/** What each project has used so far in `period`. */
|
|
10
|
+
projects: { project_id: string; used_micros: string }[];
|
|
11
|
+
/** The credit-log lines of the last up to 14 UTC days, grouped by day, project and meter, oldest day first. */
|
|
12
|
+
days: {
|
|
13
|
+
day: string;
|
|
14
|
+
projects: {
|
|
15
|
+
project_id: string;
|
|
16
|
+
meters: {
|
|
17
|
+
environment_id: string | null;
|
|
18
|
+
meter: string;
|
|
19
|
+
unit: string;
|
|
20
|
+
rate_card_id: string;
|
|
21
|
+
quantity: string;
|
|
22
|
+
charged_micros: string;
|
|
23
|
+
}[];
|
|
18
24
|
}[];
|
|
19
25
|
}[];
|
|
20
|
-
next_cursor: string | null;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
/** A bounded read model, not a new source of accounting or billing authorization. */
|
|
@@ -38,53 +43,77 @@ export function assertOrganizationCreditUsage(
|
|
|
38
43
|
typeof input === "string" && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(input);
|
|
39
44
|
const amount = (input: unknown) =>
|
|
40
45
|
typeof input === "string" && /^(0|[1-9][0-9]{0,37})$/u.test(input);
|
|
41
|
-
const
|
|
46
|
+
const instant = (input: unknown) =>
|
|
47
|
+
typeof input === "string" &&
|
|
48
|
+
Number.isFinite(Date.parse(input)) &&
|
|
49
|
+
new Date(input).toISOString() === input;
|
|
50
|
+
const report = record(value, "organization_id,as_of,unit,period,from,projects,days");
|
|
42
51
|
if (
|
|
43
52
|
!identifier(report["organization_id"]) ||
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
typeof report["as_of"] !== "string" ||
|
|
47
|
-
!Number.isFinite(Date.parse(report["as_of"])) ||
|
|
48
|
-
new Date(report["as_of"]).toISOString() !== report["as_of"] ||
|
|
53
|
+
!instant(report["as_of"]) ||
|
|
54
|
+
!instant(report["from"]) ||
|
|
49
55
|
report["unit"] !== "microcredits" ||
|
|
50
|
-
|
|
51
|
-
report["
|
|
52
|
-
|
|
56
|
+
typeof report["period"] !== "string" ||
|
|
57
|
+
!/^20[0-9]{2}-(0[1-9]|1[0-2])$/u.test(report["period"]) ||
|
|
58
|
+
!Array.isArray(report["projects"]) ||
|
|
59
|
+
report["projects"].length > 1000 ||
|
|
60
|
+
!Array.isArray(report["days"]) ||
|
|
61
|
+
report["days"].length > 15
|
|
53
62
|
)
|
|
54
63
|
throw new TypeError("Invalid usage report");
|
|
55
64
|
let previous = "";
|
|
56
|
-
for (const input of report["
|
|
57
|
-
const project = record(input, "project_id,
|
|
65
|
+
for (const input of report["projects"]) {
|
|
66
|
+
const project = record(input, "project_id,used_micros");
|
|
58
67
|
if (
|
|
59
68
|
!identifier(project["project_id"]) ||
|
|
60
69
|
String(project["project_id"]) <= previous ||
|
|
61
|
-
!amount(project["
|
|
62
|
-
!Array.isArray(project["meters"]) ||
|
|
63
|
-
project["meters"].length > 1000
|
|
70
|
+
!amount(project["used_micros"])
|
|
64
71
|
)
|
|
65
72
|
throw new TypeError("Invalid usage project");
|
|
66
73
|
previous = String(project["project_id"]);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
}
|
|
75
|
+
let previousDay = "";
|
|
76
|
+
for (const input of report["days"]) {
|
|
77
|
+
const day = record(input, "day,projects");
|
|
78
|
+
if (
|
|
79
|
+
typeof day["day"] !== "string" ||
|
|
80
|
+
!/^20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/u.test(day["day"]) ||
|
|
81
|
+
day["day"] <= previousDay ||
|
|
82
|
+
!Array.isArray(day["projects"]) ||
|
|
83
|
+
day["projects"].length > 1000
|
|
84
|
+
)
|
|
85
|
+
throw new TypeError("Invalid usage day");
|
|
86
|
+
previousDay = day["day"];
|
|
87
|
+
previous = "";
|
|
88
|
+
for (const projectInput of day["projects"]) {
|
|
89
|
+
const project = record(projectInput, "project_id,meters");
|
|
79
90
|
if (
|
|
80
|
-
|
|
81
|
-
|
|
91
|
+
!identifier(project["project_id"]) ||
|
|
92
|
+
String(project["project_id"]) <= previous ||
|
|
93
|
+
!Array.isArray(project["meters"]) ||
|
|
94
|
+
project["meters"].length > 1000
|
|
82
95
|
)
|
|
83
|
-
throw new TypeError("
|
|
96
|
+
throw new TypeError("Invalid usage project");
|
|
97
|
+
previous = String(project["project_id"]);
|
|
98
|
+
for (const meterInput of project["meters"]) {
|
|
99
|
+
const meter = record(
|
|
100
|
+
meterInput,
|
|
101
|
+
"environment_id,meter,unit,rate_card_id,quantity,charged_micros",
|
|
102
|
+
);
|
|
103
|
+
if (meter["environment_id"] !== null && !identifier(meter["environment_id"]))
|
|
104
|
+
throw new TypeError("Invalid usage environment");
|
|
105
|
+
for (const key of ["meter", "unit", "rate_card_id"])
|
|
106
|
+
if (typeof meter[key] !== "string" || !/^[A-Za-z0-9_.:/-]{1,128}$/u.test(meter[key]))
|
|
107
|
+
throw new TypeError("Invalid usage meter");
|
|
108
|
+
if (
|
|
109
|
+
typeof meter["quantity"] !== "string" ||
|
|
110
|
+
!/^(0|[1-9][0-9]{0,29})(\.[0-9]{1,12})?$/u.test(meter["quantity"]) ||
|
|
111
|
+
!amount(meter["charged_micros"])
|
|
112
|
+
)
|
|
113
|
+
throw new TypeError("Invalid usage amount");
|
|
114
|
+
}
|
|
84
115
|
}
|
|
85
116
|
}
|
|
86
|
-
if (report["next_cursor"] !== null && report["next_cursor"] !== previous)
|
|
87
|
-
throw new TypeError("Invalid usage cursor");
|
|
88
117
|
}
|
|
89
118
|
|
|
90
119
|
export interface PublishedCreditRateCard {
|
|
@@ -177,7 +206,7 @@ export function assertPublishedCreditRateCards(
|
|
|
177
206
|
}
|
|
178
207
|
}
|
|
179
208
|
|
|
180
|
-
/** Effective feature access and credit
|
|
209
|
+
/** Effective feature access and the credit account: the monthly and one-time credit and what the open reservations hold. */
|
|
181
210
|
export function assertOrganizationAccount(value: unknown): void {
|
|
182
211
|
if (
|
|
183
212
|
!value ||
|
|
@@ -203,10 +232,9 @@ export function assertOrganizationAccount(value: unknown): void {
|
|
|
203
232
|
for (const field of ["available_micros", "monthly_micros", "one_time_micros", "reserved_micros"])
|
|
204
233
|
if (typeof v[field] !== "string" || !/^(0|[1-9][0-9]{0,18})$/u.test(v[field] as string))
|
|
205
234
|
throw new TypeError("Invalid account credits");
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
)
|
|
235
|
+
const held = BigInt(String(v["reserved_micros"]));
|
|
236
|
+
const balance = BigInt(String(v["monthly_micros"])) + BigInt(String(v["one_time_micros"]));
|
|
237
|
+
if (BigInt(String(v["available_micros"])) !== (balance > held ? balance - held : 0n))
|
|
210
238
|
throw new TypeError("Account credit buckets do not balance");
|
|
211
239
|
for (const field of ["as_of", "paid_until", "next_expiry"]) {
|
|
212
240
|
if (field !== "as_of" && v[field] === null) continue;
|