@causa/workspace-google 0.18.0-beta.2 → 0.18.0-beta.4

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.
Files changed (50) hide show
  1. package/README.md +4 -0
  2. package/dist/configurations/generated.d.ts +92 -0
  3. package/dist/configurations/generated.js +172 -0
  4. package/dist/configurations/schemas/google.yaml +96 -0
  5. package/dist/emulators/firebase-storage.d.ts +9 -1
  6. package/dist/emulators/firebase-storage.js +13 -1
  7. package/dist/emulators/firestore.d.ts +9 -1
  8. package/dist/emulators/firestore.js +13 -1
  9. package/dist/emulators/identity-platform.d.ts +9 -1
  10. package/dist/emulators/identity-platform.js +13 -1
  11. package/dist/emulators/pubsub.d.ts +9 -1
  12. package/dist/emulators/pubsub.js +13 -1
  13. package/dist/emulators/spanner.d.ts +18 -2
  14. package/dist/emulators/spanner.js +26 -2
  15. package/dist/functions/emulator/start-firebase-storage.js +4 -3
  16. package/dist/functions/emulator/start-firestore.js +5 -10
  17. package/dist/functions/emulator/start-identity-platform.js +4 -7
  18. package/dist/functions/emulator/start-pubsub.call.js +10 -9
  19. package/dist/functions/emulator/start-spanner.call.js +14 -13
  20. package/dist/functions/event-topic/broker-create-trigger-cloud-run.call.js +12 -4
  21. package/dist/functions/event-topic/broker-delete-trigger-resource-cloud-run-service.call.d.ts +2 -0
  22. package/dist/functions/event-topic/broker-delete-trigger-resource-cloud-run-service.call.js +5 -0
  23. package/dist/functions/event-topic/broker-delete-trigger-resource-cloud-run-service.d.ts +10 -0
  24. package/dist/functions/event-topic/broker-delete-trigger-resource-cloud-run-service.js +19 -0
  25. package/dist/functions/event-topic/broker-delete-trigger-resource-pubsub-subscription.js +2 -1
  26. package/dist/functions/event-topic/broker-publish-events-google.call.js +26 -12
  27. package/dist/functions/event-topic/broker-wait-for-processing-pubsub.call.d.ts +2 -0
  28. package/dist/functions/event-topic/broker-wait-for-processing-pubsub.call.js +119 -0
  29. package/dist/functions/event-topic/broker-wait-for-processing-pubsub.d.ts +16 -0
  30. package/dist/functions/event-topic/broker-wait-for-processing-pubsub.js +23 -0
  31. package/dist/functions/event-topic/index.d.ts +2 -0
  32. package/dist/functions/event-topic/index.js +2 -0
  33. package/dist/functions/index.js +2 -2
  34. package/dist/services/cloud-monitoring.d.ts +44 -0
  35. package/dist/services/cloud-monitoring.js +61 -0
  36. package/dist/services/cloud-run-pubsub-trigger.d.ts +22 -1
  37. package/dist/services/cloud-run-pubsub-trigger.js +44 -5
  38. package/dist/services/cloud-run.d.ts +56 -1
  39. package/dist/services/cloud-run.js +71 -1
  40. package/dist/services/cloud-run.utils.d.ts +10 -0
  41. package/dist/services/cloud-run.utils.js +12 -0
  42. package/dist/services/google-apis.d.ts +7 -3
  43. package/dist/services/google-apis.js +1 -2
  44. package/dist/services/index.d.ts +3 -2
  45. package/dist/services/index.js +3 -2
  46. package/dist/services/pubsub.d.ts +1 -0
  47. package/dist/services/pubsub.js +1 -0
  48. package/dist/services/pubsub.utils.d.ts +9 -0
  49. package/dist/services/pubsub.utils.js +11 -0
  50. package/package.json +14 -14
@@ -2,9 +2,11 @@ export { EventTopicBrokerCreateTopicForPubSub } from './broker-create-topic-pubs
2
2
  export { EventTopicBrokerCreateTriggerForCloudRun } from './broker-create-trigger-cloud-run.js';
3
3
  export { EventTopicBrokerDeleteTopicForPubSub } from './broker-delete-topic-pubsub.js';
4
4
  export { EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole } from './broker-delete-trigger-resource-cloud-run-invoker-role.js';
5
+ export { EventTopicBrokerDeleteTriggerResourceForCloudRunService } from './broker-delete-trigger-resource-cloud-run-service.js';
5
6
  export { EventTopicBrokerDeleteTriggerResourceForPubSubSubscription } from './broker-delete-trigger-resource-pubsub-subscription.js';
6
7
  export { EventTopicBrokerDeleteTriggerResourceForServiceAccount } from './broker-delete-trigger-resource-service-account.js';
7
8
  export { EventTopicBrokerGetTopicIdForPubSub } from './broker-get-topic-id-pubsub.js';
8
9
  export { EventTopicBrokerPublishEventsForGoogle } from './broker-publish-events-google.js';
10
+ export { EventTopicBrokerWaitForProcessingForPubSub } from './broker-wait-for-processing-pubsub.js';
9
11
  export { EventTopicCreateBackfillSourceForBigQuery } from './create-backfill-source-bigquery.js';
10
12
  export { EventTopicQueryEventsForBigQuery } from './query-events-bigquery.js';
@@ -2,9 +2,11 @@ export { EventTopicBrokerCreateTopicForPubSub } from './broker-create-topic-pubs
2
2
  export { EventTopicBrokerCreateTriggerForCloudRun } from './broker-create-trigger-cloud-run.js';
3
3
  export { EventTopicBrokerDeleteTopicForPubSub } from './broker-delete-topic-pubsub.js';
4
4
  export { EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole } from './broker-delete-trigger-resource-cloud-run-invoker-role.js';
5
+ export { EventTopicBrokerDeleteTriggerResourceForCloudRunService } from './broker-delete-trigger-resource-cloud-run-service.js';
5
6
  export { EventTopicBrokerDeleteTriggerResourceForPubSubSubscription } from './broker-delete-trigger-resource-pubsub-subscription.js';
6
7
  export { EventTopicBrokerDeleteTriggerResourceForServiceAccount } from './broker-delete-trigger-resource-service-account.js';
7
8
  export { EventTopicBrokerGetTopicIdForPubSub } from './broker-get-topic-id-pubsub.js';
8
9
  export { EventTopicBrokerPublishEventsForGoogle } from './broker-publish-events-google.js';
10
+ export { EventTopicBrokerWaitForProcessingForPubSub } from './broker-wait-for-processing-pubsub.js';
9
11
  export { EventTopicCreateBackfillSourceForBigQuery } from './create-backfill-source-bigquery.js';
10
12
  export { EventTopicQueryEventsForBigQuery } from './query-events-bigquery.js';
@@ -1,6 +1,6 @@
1
1
  import { CausaListConfigurationSchemasForGoogle } from './causa/index.js';
2
2
  import { EmulatorStartForFirebaseStorage, EmulatorStartForFirestore, EmulatorStartForIdentityPlatform, EmulatorStartForPubSub, EmulatorStartForSpanner, EmulatorStopForFirebaseStorage, EmulatorStopForFirestore, EmulatorStopForIdentityPlatform, EmulatorStopForPubSub, EmulatorStopForSpanner, } from './emulator/index.js';
3
- import { EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, EventTopicCreateBackfillSourceForBigQuery, EventTopicQueryEventsForBigQuery, } from './event-topic/index.js';
3
+ import { EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForCloudRunService, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, EventTopicBrokerWaitForProcessingForPubSub, EventTopicCreateBackfillSourceForBigQuery, EventTopicQueryEventsForBigQuery, } from './event-topic/index.js';
4
4
  import { GoogleAppCheckGenerateToken } from './google-app-check/index.js';
5
5
  import { GoogleFirebaseStorageMergeRules } from './google-firebase-storage/index.js';
6
6
  import { DatabaseQueryRecordsForFirestore, GoogleFirestoreMergeRules, ModelGenerateTypeScriptDecoratorsForGoogleFirestore, ModelSchemaExtractDatabaseForGoogleFirestore, } from './google-firestore/index.js';
@@ -12,5 +12,5 @@ import { ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDesti
12
12
  import { SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, } from './secret/index.js';
13
13
  import { ServiceContainerQueryLogsForCloudRun } from './service-container/index.js';
14
14
  export function registerFunctions(context) {
15
- context.registerFunctionImplementations(CausaListConfigurationSchemasForGoogle, DatabaseQueryRecordsForFirestore, DatabaseQueryRecordsForSpanner, EmulatorStartForFirebaseStorage, EmulatorStartForFirestore, EmulatorStartForIdentityPlatform, EmulatorStartForPubSub, EmulatorStartForSpanner, EmulatorStopForFirebaseStorage, EmulatorStopForFirestore, EmulatorStopForIdentityPlatform, EmulatorStopForPubSub, EmulatorStopForSpanner, EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, EventTopicCreateBackfillSourceForBigQuery, EventTopicQueryEventsForBigQuery, GoogleAppCheckGenerateToken, GoogleFirebaseStorageMergeRules, GoogleFirestoreMergeRules, GoogleIdentityPlatformGenerateCustomToken, GoogleIdentityPlatformGenerateToken, GooglePubSubWriteTopics, GoogleServicesEnable, GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, ModelGenerateTypeScriptDecoratorsForGoogleFirestore, ModelGenerateTypeScriptDecoratorsForGoogleSpanner, ModelSchemaExtractDatabaseForGoogleFirestore, ModelSchemaExtractDatabaseForGoogleSpanner, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, ServiceContainerQueryLogsForCloudRun);
15
+ context.registerFunctionImplementations(CausaListConfigurationSchemasForGoogle, DatabaseQueryRecordsForFirestore, DatabaseQueryRecordsForSpanner, EmulatorStartForFirebaseStorage, EmulatorStartForFirestore, EmulatorStartForIdentityPlatform, EmulatorStartForPubSub, EmulatorStartForSpanner, EmulatorStopForFirebaseStorage, EmulatorStopForFirestore, EmulatorStopForIdentityPlatform, EmulatorStopForPubSub, EmulatorStopForSpanner, EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForCloudRunService, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, EventTopicBrokerWaitForProcessingForPubSub, EventTopicCreateBackfillSourceForBigQuery, EventTopicQueryEventsForBigQuery, GoogleAppCheckGenerateToken, GoogleFirebaseStorageMergeRules, GoogleFirestoreMergeRules, GoogleIdentityPlatformGenerateCustomToken, GoogleIdentityPlatformGenerateToken, GooglePubSubWriteTopics, GoogleServicesEnable, GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, ModelGenerateTypeScriptDecoratorsForGoogleFirestore, ModelGenerateTypeScriptDecoratorsForGoogleSpanner, ModelSchemaExtractDatabaseForGoogleFirestore, ModelSchemaExtractDatabaseForGoogleSpanner, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, ServiceContainerQueryLogsForCloudRun);
16
16
  }
@@ -0,0 +1,44 @@
1
+ import { WorkspaceContext } from '@causa/workspace';
2
+ import { MetricServiceClient } from '@google-cloud/monitoring';
3
+ /**
4
+ * A single metric point with its server-side end timestamp (as milliseconds since epoch) and numeric value.
5
+ */
6
+ export type MetricPoint = {
7
+ /**
8
+ * The end timestamp of the point's interval, as milliseconds since epoch.
9
+ */
10
+ readonly timestamp: number;
11
+ /**
12
+ * The numeric value of the point.
13
+ */
14
+ readonly value: number;
15
+ };
16
+ /**
17
+ * A service exposing a thin wrapper around the Cloud Monitoring API.
18
+ */
19
+ export declare class CloudMonitoringService {
20
+ /**
21
+ * The underlying Cloud Monitoring client.
22
+ */
23
+ readonly client: MetricServiceClient;
24
+ /**
25
+ * The ID of the GCP project from which metrics are read.
26
+ */
27
+ readonly projectId: string;
28
+ constructor(context: WorkspaceContext);
29
+ /**
30
+ * Fetches the data points for a single time series over the given time interval.
31
+ * Returns the points sorted in ascending timestamp order. Each point's timestamp is the end of the corresponding
32
+ * interval, in milliseconds since epoch. An empty array is returned when no series matches the filter.
33
+ *
34
+ * @param filter The Cloud Monitoring metric filter expression (e.g. `metric.type = "..."`).
35
+ * @param start The inclusive lower bound of the interval to fetch.
36
+ * @param end The exclusive upper bound of the interval to fetch.
37
+ * @param options Optional aggregation options.
38
+ * @returns The list of points, ordered by ascending timestamp.
39
+ */
40
+ listPoints(filter: string, start: Date, end: Date, options?: {
41
+ readonly alignmentPeriod?: number;
42
+ readonly aligner?: 'ALIGN_NONE' | 'ALIGN_DELTA' | 'ALIGN_RATE' | 'ALIGN_MEAN' | 'ALIGN_MAX';
43
+ }): Promise<MetricPoint[]>;
44
+ }
@@ -0,0 +1,61 @@
1
+ import { WorkspaceContext } from '@causa/workspace';
2
+ import { MetricServiceClient } from '@google-cloud/monitoring';
3
+ /**
4
+ * A service exposing a thin wrapper around the Cloud Monitoring API.
5
+ */
6
+ export class CloudMonitoringService {
7
+ /**
8
+ * The underlying Cloud Monitoring client.
9
+ */
10
+ client;
11
+ /**
12
+ * The ID of the GCP project from which metrics are read.
13
+ */
14
+ projectId;
15
+ constructor(context) {
16
+ this.projectId = context
17
+ .asConfiguration()
18
+ .getOrThrow('google.project');
19
+ this.client = new MetricServiceClient();
20
+ }
21
+ /**
22
+ * Fetches the data points for a single time series over the given time interval.
23
+ * Returns the points sorted in ascending timestamp order. Each point's timestamp is the end of the corresponding
24
+ * interval, in milliseconds since epoch. An empty array is returned when no series matches the filter.
25
+ *
26
+ * @param filter The Cloud Monitoring metric filter expression (e.g. `metric.type = "..."`).
27
+ * @param start The inclusive lower bound of the interval to fetch.
28
+ * @param end The exclusive upper bound of the interval to fetch.
29
+ * @param options Optional aggregation options.
30
+ * @returns The list of points, ordered by ascending timestamp.
31
+ */
32
+ async listPoints(filter, start, end, options = {}) {
33
+ const aggregation = options.aligner && options.alignmentPeriod
34
+ ? {
35
+ alignmentPeriod: { seconds: options.alignmentPeriod },
36
+ perSeriesAligner: options.aligner,
37
+ }
38
+ : undefined;
39
+ const [series] = await this.client.listTimeSeries({
40
+ name: `projects/${this.projectId}`,
41
+ filter,
42
+ interval: {
43
+ startTime: { seconds: Math.floor(start.getTime() / 1000) },
44
+ endTime: { seconds: Math.floor(end.getTime() / 1000) },
45
+ },
46
+ aggregation,
47
+ });
48
+ return (series.at(0)?.points ?? [])
49
+ .map(({ interval, value: rawValue }) => {
50
+ const endSeconds = Number(interval?.endTime?.seconds ?? 0);
51
+ const endNanos = interval?.endTime?.nanos ?? 0;
52
+ const timestamp = endSeconds * 1000 + Math.floor(endNanos / 1e6);
53
+ const value = Number(rawValue?.int64Value ??
54
+ rawValue?.doubleValue ??
55
+ rawValue?.distributionValue?.count ??
56
+ 0);
57
+ return { timestamp, value };
58
+ })
59
+ .sort((a, b) => a.timestamp - b.timestamp);
60
+ }
61
+ }
@@ -1,4 +1,5 @@
1
1
  import { WorkspaceContext } from '@causa/workspace';
2
+ import { type CloudRunServiceOverrides } from './cloud-run.js';
2
3
  /**
3
4
  * A service that manages (creates) Pub/Sub triggers pointing to Cloud Run services.
4
5
  * This is used for backfilling operations. The reason it is a service is to persist temporary service accounts and IAM
@@ -82,6 +83,15 @@ export declare class CloudRunPubSubTriggerService {
82
83
  * as part of the backfill cleaning.
83
84
  */
84
85
  private createSubscription;
86
+ /**
87
+ * Creates a temporary copy of a Cloud Run service to receive backfill traffic in isolation.
88
+ *
89
+ * @param backfillId The ID of the backfilling operation.
90
+ * @param sourceService The ID of the Cloud Run service to copy.
91
+ * @param overrides The overrides to apply to the copy.
92
+ * @returns The ID of the created service copy.
93
+ */
94
+ private createServiceCopy;
85
95
  /**
86
96
  * Creates a Pub/Sub trigger (push subscription) pointing to a Cloud Run service.
87
97
  * This requires several resources, such as:
@@ -90,12 +100,23 @@ export declare class CloudRunPubSubTriggerService {
90
100
  * - A Pub/Sub subscription that will push messages to the Cloud Run service.
91
101
  * The IDs of these resources are returned, and should be deleted as part of the backfill cleaning.
92
102
  *
103
+ * When `options.clone` is set, a temporary copy of the Cloud Run service is created and used as the trigger target
104
+ * instead of the live service. This isolates the backfill load (in particular its scaling) from production. The copy
105
+ * is added to the list of resources to clean up. The invoker IAM binding is not tracked separately because deleting
106
+ * the service removes it.
107
+ *
93
108
  * @param backfillId The ID of the backfilling operation.
94
109
  * @param topicId The ID of the Pub/Sub topic to subscribe to.
95
110
  * @param serviceId The ID of the Cloud Run service to invoke.
96
111
  * @param path The HTTP endpoint of the Cloud Run service to invoke.
112
+ * @param options Options for the trigger creation.
97
113
  * @returns The IDs of the resources that were created. They should be added to the list of resources to delete as
98
114
  * part of the backfill cleaning.
99
115
  */
100
- create(backfillId: string, topicId: string, serviceId: string, path: string): Promise<string[]>;
116
+ create(backfillId: string, topicId: string, serviceId: string, path: string, options?: {
117
+ /**
118
+ * When set, a temporary copy of the service is created with these overrides and used as the trigger target.
119
+ */
120
+ clone?: CloudRunServiceOverrides;
121
+ }): Promise<string[]>;
101
122
  }
@@ -3,7 +3,7 @@ import { EventTopicTriggerCreationError } from '@causa/workspace-core';
3
3
  import { Subscription } from '@google-cloud/pubsub';
4
4
  import { randomBytes } from 'crypto';
5
5
  import { grpc } from 'google-gax';
6
- import { CloudRunService } from './cloud-run.js';
6
+ import { CloudRunService, IngressTraffic, shortServiceId, } from './cloud-run.js';
7
7
  import { IamService } from './iam.js';
8
8
  import { PubSubService } from './pubsub.js';
9
9
  /**
@@ -143,6 +143,32 @@ export class CloudRunPubSubTriggerService {
143
143
  });
144
144
  return subscriptionId;
145
145
  }
146
+ /**
147
+ * Creates a temporary copy of a Cloud Run service to receive backfill traffic in isolation.
148
+ *
149
+ * @param backfillId The ID of the backfilling operation.
150
+ * @param sourceService The ID of the Cloud Run service to copy.
151
+ * @param overrides The overrides to apply to the copy.
152
+ * @returns The ID of the created service copy.
153
+ */
154
+ async createServiceCopy(backfillId, sourceService, overrides) {
155
+ const sourceName = shortServiceId(sourceService);
156
+ const suffix = randomBytes(3).toString('hex');
157
+ const maxSourceNameLength = 63 - `backfill-${backfillId}--${suffix}`.length;
158
+ const truncatedSourceName = sourceName
159
+ .slice(0, maxSourceNameLength)
160
+ .replace(/-+$/, '');
161
+ const name = `backfill-${backfillId}-${truncatedSourceName}-${suffix}`;
162
+ this.logger.info(`🚀 Creating temporary Cloud Run service '${name}' as a copy of '${sourceService}' for the backfill.`);
163
+ const service = await this.cloudRunService.copy(sourceService, name, {
164
+ overrides: {
165
+ ...overrides,
166
+ ingress: IngressTraffic.INGRESS_TRAFFIC_INTERNAL_ONLY,
167
+ labels: { 'causa-backfill-id': backfillId },
168
+ },
169
+ });
170
+ return service.name ?? '';
171
+ }
146
172
  /**
147
173
  * Creates a Pub/Sub trigger (push subscription) pointing to a Cloud Run service.
148
174
  * This requires several resources, such as:
@@ -151,26 +177,39 @@ export class CloudRunPubSubTriggerService {
151
177
  * - A Pub/Sub subscription that will push messages to the Cloud Run service.
152
178
  * The IDs of these resources are returned, and should be deleted as part of the backfill cleaning.
153
179
  *
180
+ * When `options.clone` is set, a temporary copy of the Cloud Run service is created and used as the trigger target
181
+ * instead of the live service. This isolates the backfill load (in particular its scaling) from production. The copy
182
+ * is added to the list of resources to clean up. The invoker IAM binding is not tracked separately because deleting
183
+ * the service removes it.
184
+ *
154
185
  * @param backfillId The ID of the backfilling operation.
155
186
  * @param topicId The ID of the Pub/Sub topic to subscribe to.
156
187
  * @param serviceId The ID of the Cloud Run service to invoke.
157
188
  * @param path The HTTP endpoint of the Cloud Run service to invoke.
189
+ * @param options Options for the trigger creation.
158
190
  * @returns The IDs of the resources that were created. They should be added to the list of resources to delete as
159
191
  * part of the backfill cleaning.
160
192
  */
161
- async create(backfillId, topicId, serviceId, path) {
193
+ async create(backfillId, topicId, serviceId, path, options = {}) {
194
+ const { clone } = options;
162
195
  const resourceIds = [];
163
196
  try {
197
+ let targetServiceId = serviceId;
198
+ if (clone) {
199
+ targetServiceId = await this.createServiceCopy(backfillId, serviceId, clone);
200
+ resourceIds.push(targetServiceId);
201
+ }
164
202
  const pubSubInvokerServiceAccount = await this.getBackfillInvokerServiceAccount(backfillId);
165
203
  if (pubSubInvokerServiceAccount.resourceId) {
166
204
  resourceIds.push(pubSubInvokerServiceAccount.resourceId);
167
205
  }
168
206
  const { serviceAccountEmail } = pubSubInvokerServiceAccount;
169
- const iamResourceId = await this.grantPubSubInvokerRole(serviceId, serviceAccountEmail);
170
- if (iamResourceId) {
207
+ const iamResourceId = await this.grantPubSubInvokerRole(targetServiceId, serviceAccountEmail);
208
+ // For a temporary service copy, deleting the service removes the binding along with it.
209
+ if (iamResourceId && !clone) {
171
210
  resourceIds.push(iamResourceId);
172
211
  }
173
- const subscriptionId = await this.createSubscription(backfillId, topicId, serviceId, path, serviceAccountEmail);
212
+ const subscriptionId = await this.createSubscription(backfillId, topicId, targetServiceId, path, serviceAccountEmail);
174
213
  resourceIds.push(subscriptionId);
175
214
  return resourceIds;
176
215
  }
@@ -1,4 +1,34 @@
1
- import { ServicesClient } from '@google-cloud/run';
1
+ import { ServicesClient, protos } from '@google-cloud/run';
2
+ export { CLOUD_RUN_SERVICE_ID_REGEX, shortServiceId, } from './cloud-run.utils.js';
3
+ /**
4
+ * The possible ingress settings for a Cloud Run service.
5
+ */
6
+ export declare const IngressTraffic: typeof protos.google.cloud.run.v2.IngressTraffic;
7
+ /**
8
+ * Overrides that can be applied to a Cloud Run service copy.
9
+ */
10
+ export type CloudRunServiceOverrides = {
11
+ /**
12
+ * The minimum number of instances for the service.
13
+ */
14
+ minInstanceCount?: number;
15
+ /**
16
+ * The maximum number of instances for the service.
17
+ */
18
+ maxInstanceCount?: number;
19
+ /**
20
+ * The maximum number of concurrent requests per instance.
21
+ */
22
+ requestConcurrency?: number;
23
+ /**
24
+ * The ingress setting for the copy. Defaults to the source service's ingress when not set.
25
+ */
26
+ ingress?: protos.google.cloud.run.v2.IngressTraffic;
27
+ /**
28
+ * Labels to set on the copy, merged on top of the source service's labels.
29
+ */
30
+ labels?: Record<string, string>;
31
+ };
2
32
  /**
3
33
  * A service to manage Cloud Run services.
4
34
  */
@@ -16,6 +46,31 @@ export declare class CloudRunService {
16
46
  * @returns The URI at which the service is available.
17
47
  */
18
48
  getServiceUri(serviceId: string): Promise<string>;
49
+ /**
50
+ * Creates a copy of an existing Cloud Run service.
51
+ * The copy reuses the source service's revision template (image, environment, runtime service account, etc.), ingress,
52
+ * and labels, unless overridden through the options.
53
+ *
54
+ * @param sourceService The ID of the Cloud Run service to copy.
55
+ * This should be in the format `projects/<projectId>/locations/<location>/services/<name>`.
56
+ * @param name The name of the service copy to create.
57
+ * @param options Options for the copy.
58
+ * @returns The created service copy.
59
+ */
60
+ copy(sourceService: string, name: string, options?: {
61
+ /**
62
+ * Overrides to apply to the copy. Unset scaling properties are inherited from the source service.
63
+ */
64
+ overrides?: CloudRunServiceOverrides;
65
+ }): Promise<protos.google.cloud.run.v2.IService>;
66
+ /**
67
+ * Deletes a Cloud Run service.
68
+ * This tolerates the service not existing (e.g. when it was already deleted).
69
+ *
70
+ * @param name The ID of the Cloud Run service to delete.
71
+ * This should be in the format `projects/<projectId>/locations/<location>/services/<name>`.
72
+ */
73
+ delete(name: string): Promise<void>;
19
74
  /**
20
75
  * Allows a service account to call a Cloud Run service by editing the IAM policy bindings.
21
76
  *
@@ -1,5 +1,11 @@
1
- import { ServicesClient } from '@google-cloud/run';
1
+ import { ServicesClient, protos } from '@google-cloud/run';
2
2
  import { grpc } from 'google-gax';
3
+ import { CLOUD_RUN_SERVICE_ID_REGEX } from './cloud-run.utils.js';
4
+ export { CLOUD_RUN_SERVICE_ID_REGEX, shortServiceId, } from './cloud-run.utils.js';
5
+ /**
6
+ * The possible ingress settings for a Cloud Run service.
7
+ */
8
+ export const IngressTraffic = protos.google.cloud.run.v2.IngressTraffic;
3
9
  /**
4
10
  * The role used to allow a service account to call a Cloud Run service.
5
11
  */
@@ -26,6 +32,70 @@ export class CloudRunService {
26
32
  const [service] = await this.servicesClient.getService({ name: serviceId });
27
33
  return service.uri ?? '';
28
34
  }
35
+ /**
36
+ * Creates a copy of an existing Cloud Run service.
37
+ * The copy reuses the source service's revision template (image, environment, runtime service account, etc.), ingress,
38
+ * and labels, unless overridden through the options.
39
+ *
40
+ * @param sourceService The ID of the Cloud Run service to copy.
41
+ * This should be in the format `projects/<projectId>/locations/<location>/services/<name>`.
42
+ * @param name The name of the service copy to create.
43
+ * @param options Options for the copy.
44
+ * @returns The created service copy.
45
+ */
46
+ async copy(sourceService, name, options = {}) {
47
+ const match = sourceService.match(CLOUD_RUN_SERVICE_ID_REGEX);
48
+ if (!match?.groups) {
49
+ throw new Error(`Invalid Cloud Run service ID '${sourceService}'.`);
50
+ }
51
+ const { parent } = match.groups;
52
+ const [source] = await this.servicesClient.getService({
53
+ name: sourceService,
54
+ });
55
+ const { revision, ...template } = { ...source.template };
56
+ const { minInstanceCount, maxInstanceCount, requestConcurrency, ingress, labels, } = options.overrides ?? {};
57
+ if (minInstanceCount !== undefined || maxInstanceCount !== undefined) {
58
+ template.scaling = {
59
+ ...template.scaling,
60
+ ...(minInstanceCount !== undefined ? { minInstanceCount } : {}),
61
+ ...(maxInstanceCount !== undefined ? { maxInstanceCount } : {}),
62
+ };
63
+ }
64
+ if (requestConcurrency !== undefined) {
65
+ template.maxInstanceRequestConcurrency = requestConcurrency;
66
+ }
67
+ const service = {
68
+ template,
69
+ ingress: ingress ?? source.ingress,
70
+ labels: { ...source.labels, ...labels },
71
+ };
72
+ const [operation] = await this.servicesClient.createService({
73
+ parent,
74
+ serviceId: name,
75
+ service,
76
+ });
77
+ const [created] = await operation.promise();
78
+ return created;
79
+ }
80
+ /**
81
+ * Deletes a Cloud Run service.
82
+ * This tolerates the service not existing (e.g. when it was already deleted).
83
+ *
84
+ * @param name The ID of the Cloud Run service to delete.
85
+ * This should be in the format `projects/<projectId>/locations/<location>/services/<name>`.
86
+ */
87
+ async delete(name) {
88
+ try {
89
+ const [operation] = await this.servicesClient.deleteService({ name });
90
+ await operation.promise();
91
+ }
92
+ catch (error) {
93
+ if (error.code === grpc.status.NOT_FOUND) {
94
+ return;
95
+ }
96
+ throw error;
97
+ }
98
+ }
29
99
  /**
30
100
  * Allows a service account to call a Cloud Run service by editing the IAM policy bindings.
31
101
  *
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The regular expression matching a Cloud Run service resource ID
3
+ * (e.g. `projects/<projectId>/locations/<location>/services/<name>`).
4
+ * The `parent` and `name` named groups respectively capture the parent location resource and the short service name.
5
+ */
6
+ export declare const CLOUD_RUN_SERVICE_ID_REGEX: RegExp;
7
+ /**
8
+ * Returns the short service ID (the last path segment) for a full service resource name.
9
+ */
10
+ export declare function shortServiceId(id: string): string;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The regular expression matching a Cloud Run service resource ID
3
+ * (e.g. `projects/<projectId>/locations/<location>/services/<name>`).
4
+ * The `parent` and `name` named groups respectively capture the parent location resource and the short service name.
5
+ */
6
+ export const CLOUD_RUN_SERVICE_ID_REGEX = /^(?<parent>projects\/[\w-]+\/locations\/[\w-]+)\/services\/(?<name>[\w-]+)$/;
7
+ /**
8
+ * Returns the short service ID (the last path segment) for a full service resource name.
9
+ */
10
+ export function shortServiceId(id) {
11
+ return id.split('/').at(-1) ?? id;
12
+ }
@@ -1,7 +1,10 @@
1
1
  import { WorkspaceContext } from '@causa/workspace';
2
- import { AuthClient } from 'google-auth-library';
3
- import { GoogleApis } from 'googleapis';
2
+ import { Common, GoogleApis } from 'googleapis';
4
3
  import type { ApiClient, OptionsOfApiClient } from './google-apis.types.js';
4
+ /**
5
+ * The type of authentication client returned by `googleapis`.
6
+ */
7
+ type AnyAuthClient = Awaited<ReturnType<Common.GoogleAuth['getClient']>>;
5
8
  /**
6
9
  * A service that exposes the lowest level of Google API clients, from `googleapis`.
7
10
  * Those might be needed in last resort, when no higher level client exists for an API.
@@ -21,7 +24,7 @@ export declare class GoogleApisService {
21
24
  *
22
25
  * @returns The auth client.
23
26
  */
24
- getAuthClient(): Promise<AuthClient>;
27
+ getAuthClient(): Promise<AnyAuthClient>;
25
28
  /**
26
29
  * Creates a new client for one of Google's APIs.
27
30
  * Authentication is automatically configured.
@@ -33,3 +36,4 @@ export declare class GoogleApisService {
33
36
  */
34
37
  getClient<const T extends keyof GoogleApis, const V extends string>(api: T, version: V, arg: Omit<OptionsOfApiClient<T, V>, 'auth' | 'version'>): Promise<ApiClient<T, V>>;
35
38
  }
39
+ export {};
@@ -1,6 +1,5 @@
1
1
  import { WorkspaceContext } from '@causa/workspace';
2
- import { AuthClient } from 'google-auth-library';
3
- import { GoogleApis, google } from 'googleapis';
2
+ import { Common, GoogleApis, google } from 'googleapis';
4
3
  /**
5
4
  * A service that exposes the lowest level of Google API clients, from `googleapis`.
6
5
  * Those might be needed in last resort, when no higher level client exists for an API.
@@ -1,6 +1,7 @@
1
1
  export { BigQueryService } from './bigquery.js';
2
+ export { CloudMonitoringService, type MetricPoint, } from './cloud-monitoring.js';
2
3
  export { CloudRunPubSubTriggerService } from './cloud-run-pubsub-trigger.js';
3
- export { CloudRunService } from './cloud-run.js';
4
+ export { CLOUD_RUN_SERVICE_ID_REGEX, CloudRunService, IngressTraffic, shortServiceId, type CloudRunServiceOverrides, } from './cloud-run.js';
4
5
  export * from './firebase-app.errors.js';
5
6
  export { FirebaseAppService } from './firebase-app.js';
6
7
  export type { FirebaseAppType } from './firebase-app.js';
@@ -9,7 +10,7 @@ export { GcloudEmulatorService } from './gcloud-emulator.js';
9
10
  export { GoogleApisService } from './google-apis.js';
10
11
  export { IamService } from './iam.js';
11
12
  export { LoggingService } from './logging.js';
12
- export { PubSubService } from './pubsub.js';
13
+ export { PUBSUB_SUBSCRIPTION_ID_REGEX, PubSubService, shortSubscriptionId, } from './pubsub.js';
13
14
  export { ResourceManagerService } from './resource-manager.js';
14
15
  export { GoogleSecretManagerService } from './secret-manager.js';
15
16
  export * from './storage.errors.js';
@@ -1,6 +1,7 @@
1
1
  export { BigQueryService } from './bigquery.js';
2
+ export { CloudMonitoringService, } from './cloud-monitoring.js';
2
3
  export { CloudRunPubSubTriggerService } from './cloud-run-pubsub-trigger.js';
3
- export { CloudRunService } from './cloud-run.js';
4
+ export { CLOUD_RUN_SERVICE_ID_REGEX, CloudRunService, IngressTraffic, shortServiceId, } from './cloud-run.js';
4
5
  export * from './firebase-app.errors.js';
5
6
  export { FirebaseAppService } from './firebase-app.js';
6
7
  export { FirebaseEmulatorService } from './firebase-emulator.js';
@@ -8,7 +9,7 @@ export { GcloudEmulatorService } from './gcloud-emulator.js';
8
9
  export { GoogleApisService } from './google-apis.js';
9
10
  export { IamService } from './iam.js';
10
11
  export { LoggingService } from './logging.js';
11
- export { PubSubService } from './pubsub.js';
12
+ export { PUBSUB_SUBSCRIPTION_ID_REGEX, PubSubService, shortSubscriptionId, } from './pubsub.js';
12
13
  export { ResourceManagerService } from './resource-manager.js';
13
14
  export { GoogleSecretManagerService } from './secret-manager.js';
14
15
  export * from './storage.errors.js';
@@ -1,6 +1,7 @@
1
1
  import { WorkspaceContext } from '@causa/workspace';
2
2
  import { PubSub } from '@google-cloud/pubsub';
3
3
  import { iam_v1 } from 'googleapis';
4
+ export { PUBSUB_SUBSCRIPTION_ID_REGEX, shortSubscriptionId, } from './pubsub.utils.js';
4
5
  /**
5
6
  * A service for managing Pub/Sub resources
6
7
  */
@@ -2,6 +2,7 @@ import { WorkspaceContext } from '@causa/workspace';
2
2
  import { PubSub } from '@google-cloud/pubsub';
3
3
  import { iam_v1 } from 'googleapis';
4
4
  import { ResourceManagerService } from './resource-manager.js';
5
+ export { PUBSUB_SUBSCRIPTION_ID_REGEX, shortSubscriptionId, } from './pubsub.utils.js';
5
6
  /**
6
7
  * A service for managing Pub/Sub resources
7
8
  */
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The regular expression matching a Pub/Sub subscription resource ID
3
+ * (e.g. `projects/<projectId>/subscriptions/<name>`).
4
+ */
5
+ export declare const PUBSUB_SUBSCRIPTION_ID_REGEX: RegExp;
6
+ /**
7
+ * Returns the short subscription ID (the last path segment) for a full subscription resource name.
8
+ */
9
+ export declare function shortSubscriptionId(id: string): string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The regular expression matching a Pub/Sub subscription resource ID
3
+ * (e.g. `projects/<projectId>/subscriptions/<name>`).
4
+ */
5
+ export const PUBSUB_SUBSCRIPTION_ID_REGEX = /^projects\/[\w-]+\/subscriptions\/[\w-]+$/;
6
+ /**
7
+ * Returns the short subscription ID (the last path segment) for a full subscription resource name.
8
+ */
9
+ export function shortSubscriptionId(id) {
10
+ return id.split('/').at(-1) ?? id;
11
+ }