@causa/workspace-google 1.3.1 → 1.4.0-beta.2

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 (98) hide show
  1. package/README.md +23 -0
  2. package/dist/functions/event-topic/create-backfill-source-bigquery.call.js +2 -2
  3. package/dist/functions/event-topic/query-events-bigquery.call.js +2 -2
  4. package/dist/functions/google-app-check/generate-token.js +3 -1
  5. package/dist/functions/google-firebase-storage/merge-rules.js +3 -1
  6. package/dist/functions/google-firestore/generate-typescript-decorators.js +2 -1
  7. package/dist/functions/google-firestore/merge-rules.js +3 -1
  8. package/dist/functions/google-identity-platform/generate-custom-token.js +7 -2
  9. package/dist/functions/google-pubsub/index.d.ts +1 -1
  10. package/dist/functions/google-pubsub/index.js +1 -1
  11. package/dist/functions/google-pubsub/write-topics.d.ts +7 -0
  12. package/dist/functions/google-pubsub/write-topics.js +17 -3
  13. package/dist/functions/google-services/enable.js +3 -1
  14. package/dist/functions/google-spanner/generate-typescript-decorators.js +2 -1
  15. package/dist/functions/google-spanner/list-databases.d.ts +1 -1
  16. package/dist/functions/google-spanner/write-databases.js +7 -2
  17. package/dist/functions/graph/fetch-environment-metric-series.call.d.ts +3 -0
  18. package/dist/functions/graph/fetch-environment-metric-series.call.js +107 -0
  19. package/dist/functions/graph/fetch-environment-metric-series.d.ts +23 -0
  20. package/dist/functions/graph/fetch-environment-metric-series.js +40 -0
  21. package/dist/functions/graph/get-environment-provider.d.ts +11 -0
  22. package/dist/functions/graph/get-environment-provider.js +20 -0
  23. package/dist/functions/graph/index.d.ts +3 -0
  24. package/dist/functions/graph/index.js +3 -0
  25. package/dist/functions/graph/list-rules.d.ts +11 -0
  26. package/dist/functions/graph/list-rules.js +16 -0
  27. package/dist/functions/index.js +2 -1
  28. package/dist/graph/cloud-run.d.ts +98 -0
  29. package/dist/graph/cloud-run.js +102 -0
  30. package/dist/graph/environment/fetchers/cloud-monitoring-alerts.d.ts +6 -0
  31. package/dist/graph/environment/fetchers/cloud-monitoring-alerts.js +106 -0
  32. package/dist/graph/environment/fetchers/cloud-monitoring.d.ts +5 -0
  33. package/dist/graph/environment/fetchers/cloud-monitoring.js +13 -0
  34. package/dist/graph/environment/fetchers/cloud-run.d.ts +5 -0
  35. package/dist/graph/environment/fetchers/cloud-run.js +88 -0
  36. package/dist/graph/environment/fetchers/cloud-scheduler.d.ts +5 -0
  37. package/dist/graph/environment/fetchers/cloud-scheduler.js +95 -0
  38. package/dist/graph/environment/fetchers/console-links.d.ts +14 -0
  39. package/dist/graph/environment/fetchers/console-links.js +132 -0
  40. package/dist/graph/environment/fetchers/error-reporting.d.ts +5 -0
  41. package/dist/graph/environment/fetchers/error-reporting.js +98 -0
  42. package/dist/graph/environment/fetchers/index.d.ts +5 -0
  43. package/dist/graph/environment/fetchers/index.js +17 -0
  44. package/dist/graph/environment/index.d.ts +9 -0
  45. package/dist/graph/environment/index.js +9 -0
  46. package/dist/graph/environment/load-balancer-backends.d.ts +82 -0
  47. package/dist/graph/environment/load-balancer-backends.js +126 -0
  48. package/dist/graph/environment/monitoring-alerts.d.ts +60 -0
  49. package/dist/graph/environment/monitoring-alerts.js +67 -0
  50. package/dist/graph/environment/monitoring-catalog.d.ts +95 -0
  51. package/dist/graph/environment/monitoring-catalog.js +356 -0
  52. package/dist/graph/environment/monitoring-mappings.d.ts +133 -0
  53. package/dist/graph/environment/monitoring-mappings.js +97 -0
  54. package/dist/graph/environment/monitoring-query.d.ts +116 -0
  55. package/dist/graph/environment/monitoring-query.js +314 -0
  56. package/dist/graph/environment/monitoring-values.d.ts +41 -0
  57. package/dist/graph/environment/monitoring-values.js +119 -0
  58. package/dist/graph/environment/resource-resolvers.d.ts +7 -0
  59. package/dist/graph/environment/resource-resolvers.js +57 -0
  60. package/dist/graph/environment/resources.d.ts +45 -0
  61. package/dist/graph/environment/resources.js +49 -0
  62. package/dist/graph/ids.d.ts +35 -0
  63. package/dist/graph/ids.js +44 -0
  64. package/dist/graph/index.d.ts +7 -0
  65. package/dist/graph/index.js +7 -0
  66. package/dist/graph/rules/configuration/enqueues-assumed-from-google-task-trigger.d.ts +6 -0
  67. package/dist/graph/rules/configuration/enqueues-assumed-from-google-task-trigger.js +39 -0
  68. package/dist/graph/rules/configuration/firestore.d.ts +11 -0
  69. package/dist/graph/rules/configuration/firestore.js +93 -0
  70. package/dist/graph/rules/configuration/index.d.ts +5 -0
  71. package/dist/graph/rules/configuration/index.js +11 -0
  72. package/dist/graph/rules/configuration/service-container.d.ts +13 -0
  73. package/dist/graph/rules/configuration/service-container.js +123 -0
  74. package/dist/graph/rules/terraform/api-router.d.ts +15 -0
  75. package/dist/graph/rules/terraform/api-router.js +117 -0
  76. package/dist/graph/rules/terraform/cloud-run-cron-triggers.d.ts +11 -0
  77. package/dist/graph/rules/terraform/cloud-run-cron-triggers.js +67 -0
  78. package/dist/graph/rules/terraform/cloud-run-grants.d.ts +16 -0
  79. package/dist/graph/rules/terraform/cloud-run-grants.js +118 -0
  80. package/dist/graph/rules/terraform/cloud-run-module.d.ts +6 -0
  81. package/dist/graph/rules/terraform/cloud-run-module.js +29 -0
  82. package/dist/graph/rules/terraform/cloud-run-pubsub-triggers.d.ts +14 -0
  83. package/dist/graph/rules/terraform/cloud-run-pubsub-triggers.js +103 -0
  84. package/dist/graph/rules/terraform/cloud-run-services.d.ts +12 -0
  85. package/dist/graph/rules/terraform/cloud-run-services.js +61 -0
  86. package/dist/graph/rules/terraform/cloud-run-tasks-triggers.d.ts +14 -0
  87. package/dist/graph/rules/terraform/cloud-run-tasks-triggers.js +97 -0
  88. package/dist/graph/rules/terraform/event-topics.d.ts +18 -0
  89. package/dist/graph/rules/terraform/event-topics.js +193 -0
  90. package/dist/graph/rules/terraform/index.d.ts +5 -0
  91. package/dist/graph/rules/terraform/index.js +13 -0
  92. package/dist/graph/rules/terraform/spanner-databases.d.ts +17 -0
  93. package/dist/graph/rules/terraform/spanner-databases.js +180 -0
  94. package/dist/graph/spanner.d.ts +106 -0
  95. package/dist/graph/spanner.js +118 -0
  96. package/dist/graph/terraform-modules.d.ts +87 -0
  97. package/dist/graph/terraform-modules.js +107 -0
  98. package/package.json +30 -29
package/README.md CHANGED
@@ -74,6 +74,29 @@ This module implements the `DatabaseQueryRecords`, `ServiceContainerQueryLogs`,
74
74
  - `ServiceContainerQueryLogs` is supported when `serviceContainer.platform` is `google.cloudRun`. It queries Cloud Logging for log entries emitted by the service's Cloud Run revisions. By default, only the last hour of logs and up to 1 000 entries are returned.
75
75
  - `EventTopicQueryEvents` is supported when `events.broker` is `google.pubSub`, `events.format` is `json`, and `google.pubSub.bigQueryStorage.rawEventsDatasetId` is set. It reads events from the topic's BigQuery raw events table. Same defaults as for log queries: last hour and up to 1 000 events.
76
76
 
77
+ ### Architecture graph
78
+
79
+ This module contributes to the architecture graph extracted by `cs graph extract` (see the core module):
80
+
81
+ - Some rules read the workspace configuration and model: the Firestore database and the collections schemas bind to (`firestoreFromConfiguration`), the triggers of type `google.pubSub` and the entities projects access through the databases and collections listed in `serviceContainer.outputs` (`googleServiceContainerFromConfiguration`), and the triggers of type `google.task(s)`.
82
+ - Other rules mirror the resources created by the Causa Terraform modules for Google Cloud, one rule per module (Cloud Run services, event topics, Spanner databases, and API router), by joining the arguments of their blocks with the configuration the modules read. Each resource is linked by a `deploys` edge from the projects applying the block.
83
+
84
+ Infrastructure nodes carry the Google Cloud resource they stand for in `data.resource`: its `type` (e.g. `pubsub.googleapis.com/Topic`), its `id`, i.e. its full resource name (or `idPrefix` when the full name is not known), and the `scope`, i.e. the project whose configuration should be used to render the identifier.
85
+
86
+ When the graph is enriched with environment data, this module resolves the names of Cloud Tasks queues by listing the queues of the environment. Queues that cannot be resolved are removed from their nodes. This module's fetchers then:
87
+
88
+ - read metrics of services, API routers, Pub/Sub topics and subscriptions, Cloud Tasks queues, and Spanner, Firestore, and BigQuery databases from Cloud Monitoring
89
+ - add the `deployment` of Cloud Run services, and the `lastAttempt` and `nextRun` of Cloud Scheduler jobs. They describe services and jobs as they are when the graph is enriched, with a warning when they changed after `at`.
90
+ - add `links` to the Google Cloud console for each resource
91
+ - attach the alerts open in Cloud Monitoring to the nodes of their resources. Matching an alert to a node requires the condition of its policy to keep the labels making up the resource name (e.g. `project_id`, `location`, and `service_name`), along with `monitored_resource` when the metric is written against several resource types, like `logging.googleapis.com/log_entry_count`.
92
+ - attach the open Error Reporting groups of Cloud Run services to their nodes, as alerts with the `error` severity. A group is open when it has occurrences during the last 30 days, and is not acknowledged, resolved, or muted.
93
+
94
+ The requests and latencies of API routers are also grouped by the `node` the requests were routed to, by mapping the serverless network endpoint groups of their load balancers to Cloud Run services. They are listed using the Compute Engine API (`compute.networkEndpointGroups.list`) in the projects of the API routers.
95
+
96
+ `GraphFetchEnvironmentMetricSeries` is implemented for the metrics read from Cloud Monitoring.
97
+
98
+ Cloud Monitoring exposes complete data with a delay of a few minutes. Metrics, including their time series, are read over windows ending at most 5 minutes ago.
99
+
77
100
  ### Secrets backend
78
101
 
79
102
  This module implements the `google.secretManager` secret backend, allowing fetching secrets from the Google Secret Manager service. Here are some example of how secrets with the `google.secretManager` backend should be defined:
@@ -1,4 +1,5 @@
1
1
  import { BigQueryService } from '../../services/index.js';
2
+ import { rawEventsTableName } from '../google-pubsub/index.js';
2
3
  import { BIGQUERY_SOURCE_REGEX, } from './create-backfill-source-bigquery.js';
3
4
  /**
4
5
  * Resolves the full BigQuery table ID from the function arguments and configuration.
@@ -18,8 +19,7 @@ function resolveTableId(self, context) {
18
19
  const googleConf = context.asConfiguration();
19
20
  const projectId = googleConf.getOrThrow('google.project');
20
21
  const rawEventsDatasetId = googleConf.getOrThrow('google.pubSub.bigQueryStorage.rawEventsDatasetId');
21
- const tableName = self.eventTopic.replace(/[-\.]/g, '_');
22
- return `${projectId}.${rawEventsDatasetId}.${tableName}`;
22
+ return `${projectId}.${rawEventsDatasetId}.${rawEventsTableName(self.eventTopic)}`;
23
23
  }
24
24
  export default async function call() {
25
25
  const tableId = resolveTableId(this, this._context);
@@ -1,4 +1,5 @@
1
1
  import { BigQueryService } from '../../services/index.js';
2
+ import { rawEventsTableName } from '../google-pubsub/index.js';
2
3
  /**
3
4
  * The default duration applied to `from` when it is not set, in milliseconds.
4
5
  */
@@ -13,8 +14,7 @@ export default async function call() {
13
14
  const googleConf = this._context.asConfiguration();
14
15
  const projectId = googleConf.getOrThrow('google.project');
15
16
  const rawEventsDatasetId = googleConf.getOrThrow('google.pubSub.bigQueryStorage.rawEventsDatasetId');
16
- const tableName = this.topic.replace(/[-\.]/g, '_');
17
- const tableId = `${projectId}.${rawEventsDatasetId}.${tableName}`;
17
+ const tableId = `${projectId}.${rawEventsDatasetId}.${rawEventsTableName(this.topic)}`;
18
18
  const conditions = [`publish_time >= @from`];
19
19
  const params = { from: from.toISOString() };
20
20
  if (this.to) {
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { CliCommand, CliOption } from '@causa/cli';
11
11
  import { callDeferred, WorkspaceFunction } from '@causa/workspace';
12
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
12
13
  import { AllowMissing } from '@causa/workspace/validation';
13
14
  import { IsString } from 'class-validator';
14
15
  import { appCheckCommandDefinition } from '../../cli/index.js';
@@ -48,6 +49,7 @@ If the Firebase app ID is not specified, it will:
48
49
  - If not present in the configuration, will be automatically looked up from the API.`,
49
50
  summary: 'Generates an AppCheck token.',
50
51
  outputFn: (token) => console.log(token),
51
- })
52
+ }),
53
+ PassArgumentsByReference()
52
54
  ], GoogleAppCheckGenerateToken);
53
55
  export { GoogleAppCheckGenerateToken };
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { CliCommand } from '@causa/cli';
11
11
  import { WorkspaceFunction } from '@causa/workspace';
12
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
12
13
  import { AllowMissing } from '@causa/workspace/validation';
13
14
  import { IsBoolean } from 'class-validator';
14
15
  import { firebaseStorageCommandDefinition } from '../../cli/index.js';
@@ -67,6 +68,7 @@ GoogleFirebaseStorageMergeRules = __decorate([
67
68
  Input files are looked for in the workspace using the globs defined in google.firebaseStorage.securityRulesFiles.`,
68
69
  summary: 'Merge Firebase Storage security rules into a single file.',
69
70
  outputFn: ({ securityRuleFile }) => console.log(securityRuleFile),
70
- })
71
+ }),
72
+ PassArgumentsByReference()
71
73
  ], GoogleFirebaseStorageMergeRules);
72
74
  export { GoogleFirebaseStorageMergeRules };
@@ -1,3 +1,4 @@
1
+ import { splitSchemaPath } from '@causa/workspace-core/jsonschema';
1
2
  import { ModelGenerateTypeScriptDecorators } from '@causa/workspace-typescript';
2
3
  import { addDecoratorToList, } from '@causa/workspace-typescript/code-generation';
3
4
  import micromatch from 'micromatch';
@@ -32,7 +33,7 @@ export class ModelGenerateTypeScriptDecoratorsForGoogleFirestore extends ModelGe
32
33
  if (Array.isArray(globs)) {
33
34
  const projectPath = this._context.getProjectPathOrThrow();
34
35
  const absoluteGlobs = globs.map((g) => join(projectPath, g));
35
- const schemaPath = this.schema.path.split('#')[0];
36
+ const { file: schemaPath } = splitSchemaPath(this.schema.path);
36
37
  if (!micromatch.isMatch(schemaPath, absoluteGlobs)) {
37
38
  return [];
38
39
  }
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { CliCommand } from '@causa/cli';
11
11
  import { WorkspaceFunction } from '@causa/workspace';
12
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
12
13
  import { AllowMissing } from '@causa/workspace/validation';
13
14
  import { IsBoolean } from 'class-validator';
14
15
  import { firestoreCommandDefinition } from '../../cli/index.js';
@@ -67,6 +68,7 @@ GoogleFirestoreMergeRules = __decorate([
67
68
  Input files are looked for in the workspace using the globs defined in google.firestore.securityRulesFiles.`,
68
69
  summary: 'Merge Firestore security rules into a single file.',
69
70
  outputFn: ({ securityRuleFile }) => console.log(securityRuleFile),
70
- })
71
+ }),
72
+ PassArgumentsByReference()
71
73
  ], GoogleFirestoreMergeRules);
72
74
  export { GoogleFirestoreMergeRules };
@@ -8,6 +8,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
10
  import { callDeferred, WorkspaceFunction } from '@causa/workspace';
11
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
11
12
  import { AllowMissing } from '@causa/workspace/validation';
12
13
  import { IsObject, IsString } from 'class-validator';
13
14
  /**
@@ -17,7 +18,7 @@ import { IsObject, IsString } from 'class-validator';
17
18
  * If `google.firebase.adminServiceAccount` is set, the service account will be used to sign the token. Otherwise, an
18
19
  * attempt will be made to find a valid service account in the `google.project`.
19
20
  */
20
- export class GoogleIdentityPlatformGenerateCustomToken extends WorkspaceFunction {
21
+ let GoogleIdentityPlatformGenerateCustomToken = class GoogleIdentityPlatformGenerateCustomToken extends WorkspaceFunction {
21
22
  /**
22
23
  * The ID of the user for which the custom token should be generated.
23
24
  */
@@ -32,7 +33,7 @@ export class GoogleIdentityPlatformGenerateCustomToken extends WorkspaceFunction
32
33
  _supports() {
33
34
  return true;
34
35
  }
35
- }
36
+ };
36
37
  __decorate([
37
38
  IsString(),
38
39
  __metadata("design:type", String)
@@ -42,3 +43,7 @@ __decorate([
42
43
  AllowMissing(),
43
44
  __metadata("design:type", Object)
44
45
  ], GoogleIdentityPlatformGenerateCustomToken.prototype, "claims", void 0);
46
+ GoogleIdentityPlatformGenerateCustomToken = __decorate([
47
+ PassArgumentsByReference()
48
+ ], GoogleIdentityPlatformGenerateCustomToken);
49
+ export { GoogleIdentityPlatformGenerateCustomToken };
@@ -1 +1 @@
1
- export { GooglePubSubWriteTopics } from './write-topics.js';
1
+ export { GooglePubSubWriteTopics, rawEventsTableName } from './write-topics.js';
@@ -1 +1 @@
1
- export { GooglePubSubWriteTopics } from './write-topics.js';
1
+ export { GooglePubSubWriteTopics, rawEventsTableName } from './write-topics.js';
@@ -1,5 +1,12 @@
1
1
  import { type ProcessorResult, WorkspaceFunction } from '@causa/workspace';
2
2
  import { type InfrastructureProcessor } from '@causa/workspace-core';
3
+ /**
4
+ * Returns the name of the BigQuery table storing the raw events of a topic.
5
+ *
6
+ * @param topic The ID of the topic.
7
+ * @returns The name of the BigQuery table.
8
+ */
9
+ export declare function rawEventsTableName(topic: string): string;
3
10
  /**
4
11
  * A function that uses {@link EventTopicList} to find all the topics in the workspace, and writes their configurations
5
12
  * to a directory.
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { WorkspaceFunction } from '@causa/workspace';
11
11
  import { EventTopicList, } from '@causa/workspace-core';
12
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
12
13
  import { CAUSA_FOLDER } from '@causa/workspace/initialization';
13
14
  import { AllowMissing } from '@causa/workspace/validation';
14
15
  import { IsBoolean } from 'class-validator';
@@ -18,6 +19,15 @@ import { join } from 'path';
18
19
  * The default directory where Pub/Sub topic configurations are written, relative to the workspace root.
19
20
  */
20
21
  const DEFAULT_TOPIC_CONFIGURATIONS_DIRECTORY = join(CAUSA_FOLDER, 'pubsub-topics');
22
+ /**
23
+ * Returns the name of the BigQuery table storing the raw events of a topic.
24
+ *
25
+ * @param topic The ID of the topic.
26
+ * @returns The name of the BigQuery table.
27
+ */
28
+ export function rawEventsTableName(topic) {
29
+ return topic.replace(/[-.]/g, '_');
30
+ }
21
31
  /**
22
32
  * A function that uses {@link EventTopicList} to find all the topics in the workspace, and writes their configurations
23
33
  * to a directory.
@@ -25,7 +35,7 @@ const DEFAULT_TOPIC_CONFIGURATIONS_DIRECTORY = join(CAUSA_FOLDER, 'pubsub-topics
25
35
  * topic configurations.
26
36
  * This function returns a partial configuration, such that it can be used as a processor.
27
37
  */
28
- export class GooglePubSubWriteTopics extends WorkspaceFunction {
38
+ let GooglePubSubWriteTopics = class GooglePubSubWriteTopics extends WorkspaceFunction {
29
39
  tearDown;
30
40
  /**
31
41
  * Returns the path to the directory where Pub/Sub topic configurations should be written.
@@ -56,7 +66,7 @@ export class GooglePubSubWriteTopics extends WorkspaceFunction {
56
66
  await Promise.all(topics.map(async (topic) => {
57
67
  const topicConfiguration = {
58
68
  ...topic,
59
- bigQueryTableName: topic.id.replace(/[-\.]/g, '_'),
69
+ bigQueryTableName: rawEventsTableName(topic.id),
60
70
  };
61
71
  const topicFile = join(absoluteDir, `${topic.id}.json`);
62
72
  await writeFile(topicFile, JSON.stringify(topicConfiguration));
@@ -71,9 +81,13 @@ export class GooglePubSubWriteTopics extends WorkspaceFunction {
71
81
  _supports() {
72
82
  return true;
73
83
  }
74
- }
84
+ };
75
85
  __decorate([
76
86
  IsBoolean(),
77
87
  AllowMissing(),
78
88
  __metadata("design:type", Boolean)
79
89
  ], GooglePubSubWriteTopics.prototype, "tearDown", void 0);
90
+ GooglePubSubWriteTopics = __decorate([
91
+ PassArgumentsByReference()
92
+ ], GooglePubSubWriteTopics);
93
+ export { GooglePubSubWriteTopics };
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { CliCommand } from '@causa/cli';
11
11
  import { callDeferred, WorkspaceFunction } from '@causa/workspace';
12
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
12
13
  import { AllowMissing } from '@causa/workspace/validation';
13
14
  import { IsBoolean } from 'class-validator';
14
15
  import { googleCommandDefinition } from '../../cli/index.js';
@@ -38,6 +39,7 @@ Required GCP services should be defined in the 'google.services' configuration.
38
39
  They will be enabled in the 'google.project' GCP project.`,
39
40
  summary: 'Enables the GCP services used by the current project.',
40
41
  outputFn: ({ services }) => console.log(services.join('\n')),
41
- })
42
+ }),
43
+ PassArgumentsByReference()
42
44
  ], GoogleServicesEnable);
43
45
  export { GoogleServicesEnable };
@@ -1,3 +1,4 @@
1
+ import { splitSchemaPath } from '@causa/workspace-core/jsonschema';
1
2
  import { ModelGenerateTypeScriptDecorators } from '@causa/workspace-typescript';
2
3
  import { addDecoratorToList, } from '@causa/workspace-typescript/code-generation';
3
4
  import micromatch from 'micromatch';
@@ -41,7 +42,7 @@ export class ModelGenerateTypeScriptDecoratorsForGoogleSpanner extends ModelGene
41
42
  if (Array.isArray(globs)) {
42
43
  const projectPath = this._context.getProjectPathOrThrow();
43
44
  const absoluteGlobs = globs.map((g) => join(projectPath, g));
44
- const schemaPath = this.schema.path.split('#')[0];
45
+ const { file: schemaPath } = splitSchemaPath(this.schema.path);
45
46
  if (!micromatch.isMatch(schemaPath, absoluteGlobs)) {
46
47
  return [];
47
48
  }
@@ -8,7 +8,7 @@ export type SpannerDatabase = {
8
8
  */
9
9
  id: string;
10
10
  /**
11
- * The paths (relative to the workspace root) containing DDL statements for this database.
11
+ * The absolute paths of the files containing DDL statements for this database.
12
12
  */
13
13
  ddlFiles: string[];
14
14
  /**
@@ -8,6 +8,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
10
  import { WorkspaceFunction } from '@causa/workspace';
11
+ import { PassArgumentsByReference } from '@causa/workspace/function-registry';
11
12
  import { CAUSA_FOLDER } from '@causa/workspace/initialization';
12
13
  import { AllowMissing } from '@causa/workspace/validation';
13
14
  import { IsBoolean } from 'class-validator';
@@ -25,7 +26,7 @@ const DEFAULT_DATABASE_CONFIGURATIONS_DIRECTORY = join(CAUSA_FOLDER, 'spanner-da
25
26
  * database configurations.
26
27
  * This function returns a partial configuration, such that it can be used as a processor.
27
28
  */
28
- export class GoogleSpannerWriteDatabases extends WorkspaceFunction {
29
+ let GoogleSpannerWriteDatabases = class GoogleSpannerWriteDatabases extends WorkspaceFunction {
29
30
  tearDown;
30
31
  /**
31
32
  * Returns the path to the directory where Spanner databases configurations should be written.
@@ -64,9 +65,13 @@ export class GoogleSpannerWriteDatabases extends WorkspaceFunction {
64
65
  _supports() {
65
66
  return true;
66
67
  }
67
- }
68
+ };
68
69
  __decorate([
69
70
  IsBoolean(),
70
71
  AllowMissing(),
71
72
  __metadata("design:type", Boolean)
72
73
  ], GoogleSpannerWriteDatabases.prototype, "tearDown", void 0);
74
+ GoogleSpannerWriteDatabases = __decorate([
75
+ PassArgumentsByReference()
76
+ ], GoogleSpannerWriteDatabases);
77
+ export { GoogleSpannerWriteDatabases };
@@ -0,0 +1,3 @@
1
+ import { type GraphMetricSeries } from '@causa/workspace-core';
2
+ import type { GraphFetchEnvironmentMetricSeriesForGoogle } from './fetch-environment-metric-series.js';
3
+ export default function call(this: GraphFetchEnvironmentMetricSeriesForGoogle): Promise<GraphMetricSeries>;
@@ -0,0 +1,107 @@
1
+ import { GraphMetricKind, } from '@causa/workspace-core';
2
+ import { GraphEnvironmentContext } from '@causa/workspace-core/graph';
3
+ import { monitoringMetricKind } from '../../graph/environment/monitoring-catalog.js';
4
+ import { MIN_ALIGNMENT_PERIOD, monitoringEnd, queryMonitoringMetric, } from '../../graph/environment/monitoring-query.js';
5
+ import { BucketLayoutRegistry } from '../../graph/environment/monitoring-values.js';
6
+ /**
7
+ * The steps between points from which the default one is chosen, in seconds.
8
+ */
9
+ const DEFAULT_STEPS = [
10
+ 60, 120, 300, 600, 900, 1800, 3600, 7200, 10800, 21600, 43200, 86400,
11
+ ];
12
+ /**
13
+ * The maximum number of points in a series when the step is chosen by default.
14
+ */
15
+ const DEFAULT_MAX_POINTS = 300;
16
+ /**
17
+ * Returns the default step for a series, as the smallest of {@link DEFAULT_STEPS} that results in at most
18
+ * {@link DEFAULT_MAX_POINTS} points. For longer series, a whole number of days is used.
19
+ *
20
+ * @param duration The duration of the series, in seconds.
21
+ * @returns The step, in seconds.
22
+ */
23
+ function defaultStep(duration) {
24
+ const minimum = duration / DEFAULT_MAX_POINTS;
25
+ const day = DEFAULT_STEPS[DEFAULT_STEPS.length - 1];
26
+ return (DEFAULT_STEPS.find((step) => step >= minimum) ??
27
+ Math.ceil(minimum / day) * day);
28
+ }
29
+ /**
30
+ * The maximum number of points in a series.
31
+ */
32
+ const MAX_POINTS = 10000;
33
+ export default async function call() {
34
+ const metric = this.findMetric();
35
+ if (!metric) {
36
+ throw new Error(`The metric '${this.metric}' of node '${this.node}' is not read from Cloud Monitoring.`);
37
+ }
38
+ const { start: requestedStart } = this;
39
+ if (isNaN(this.end.getTime()) || isNaN(requestedStart.getTime())) {
40
+ throw new Error('The start and end of the series must be valid dates.');
41
+ }
42
+ const end = monitoringEnd(this.end);
43
+ const duration = (end.getTime() - requestedStart.getTime()) / 1000;
44
+ const requestedStep = this.step ?? defaultStep(duration);
45
+ if (!Number.isInteger(requestedStep) ||
46
+ requestedStep < MIN_ALIGNMENT_PERIOD) {
47
+ throw new Error(`The step must be a whole number of seconds, of at least ${MIN_ALIGNMENT_PERIOD} seconds.`);
48
+ }
49
+ const requestedCount = Math.floor(duration / requestedStep);
50
+ if (requestedCount < 1 || requestedCount > MAX_POINTS) {
51
+ throw new Error(`The series must contain between 1 and ${MAX_POINTS} points, but contains ${Math.max(requestedCount, 0)}.`);
52
+ }
53
+ // Metrics sampled less often than the step are read with a larger step, and at least one point, as for the
54
+ // point-in-time value.
55
+ const step = Math.max(requestedStep, metric.minPeriod ?? 0);
56
+ const count = step === requestedStep
57
+ ? requestedCount
58
+ : Math.max(Math.floor(duration / step), 1);
59
+ if (!this.graph.environment) {
60
+ throw new Error('The graph must be enriched with environment data to fetch a series.');
61
+ }
62
+ const environment = await GraphEnvironmentContext.forEnrichedGraph(this._context, this.graph);
63
+ const layouts = new BucketLayoutRegistry();
64
+ // Warnings, e.g. about time series relating to resources missing from the graph, don't affect the values.
65
+ const { values, groupBy, errors } = await queryMonitoringMetric(environment, metric, { end, period: step, count }, layouts, { nodes: [this.node] });
66
+ if (errors.length > 0) {
67
+ throw new Error(errors.map((e) => e.message).join(' '));
68
+ }
69
+ const points = values.get(this.node) ?? new Array(count).fill(undefined);
70
+ const start = new Date(end.getTime() - count * step * 1000);
71
+ return {
72
+ start,
73
+ step,
74
+ values: points.map((p) => p?.value ?? null),
75
+ ...(groupBy
76
+ ? {
77
+ groupBy,
78
+ groups: seriesGroups(points, monitoringMetricKind(metric) === GraphMetricKind.Rate ? 0 : null),
79
+ }
80
+ : {}),
81
+ bucketLayouts: layouts.all,
82
+ };
83
+ }
84
+ /**
85
+ * Returns the series of each group, from the points of the series.
86
+ *
87
+ * @param points The points of the series.
88
+ * @param missing The value of a group in a period where the point exists but not the group, e.g. `0` for rates.
89
+ * @returns The series of each group, sorted by key.
90
+ */
91
+ function seriesGroups(points, missing) {
92
+ const groups = new Map();
93
+ points.forEach((point, index) => {
94
+ for (const { key, value } of point?.groups ?? []) {
95
+ const serialized = JSON.stringify(key);
96
+ const group = groups.get(serialized) ?? {
97
+ key,
98
+ values: points.map((p) => (p ? missing : null)),
99
+ };
100
+ groups.set(serialized, group);
101
+ group.values[index] = value;
102
+ }
103
+ });
104
+ return [...groups.entries()]
105
+ .sort(([a], [b]) => a.localeCompare(b))
106
+ .map(([, group]) => group);
107
+ }
@@ -0,0 +1,23 @@
1
+ import { GraphFetchEnvironmentMetricSeries, type GraphMetricSeries } from '@causa/workspace-core';
2
+ import { type GraphNodeEntry } from '@causa/workspace-core/graph';
3
+ import { type MonitoringMetric } from '../../graph/environment/monitoring-catalog.js';
4
+ /**
5
+ * Implements {@link GraphFetchEnvironmentMetricSeries} for the metrics read from Cloud Monitoring.
6
+ */
7
+ export declare class GraphFetchEnvironmentMetricSeriesForGoogle extends GraphFetchEnvironmentMetricSeries {
8
+ _call(): Promise<GraphMetricSeries>;
9
+ /**
10
+ * Returns the requested node, from the graph.
11
+ *
12
+ * @returns The node, or `undefined` if it does not exist.
13
+ */
14
+ findNode(): GraphNodeEntry | undefined;
15
+ /**
16
+ * Returns the Cloud Monitoring metric providing the requested metric of the node.
17
+ *
18
+ * @returns The metric, or `undefined` if the node does not exist, has no resource, or the metric is not read from
19
+ * Cloud Monitoring for it.
20
+ */
21
+ findMetric(): MonitoringMetric | undefined;
22
+ _supports(): boolean;
23
+ }
@@ -0,0 +1,40 @@
1
+ import { callDeferred } from '@causa/workspace';
2
+ import { GraphFetchEnvironmentMetricSeries, } from '@causa/workspace-core';
3
+ import { listGraphNodes, } from '@causa/workspace-core/graph';
4
+ import { MONITORING_METRICS, } from '../../graph/environment/monitoring-catalog.js';
5
+ /**
6
+ * Implements {@link GraphFetchEnvironmentMetricSeries} for the metrics read from Cloud Monitoring.
7
+ */
8
+ export class GraphFetchEnvironmentMetricSeriesForGoogle extends GraphFetchEnvironmentMetricSeries {
9
+ async _call() {
10
+ return await callDeferred(this, import.meta.url);
11
+ }
12
+ /**
13
+ * Returns the requested node, from the graph.
14
+ *
15
+ * @returns The node, or `undefined` if it does not exist.
16
+ */
17
+ findNode() {
18
+ return listGraphNodes(this.graph).find((n) => n.id === this.node);
19
+ }
20
+ /**
21
+ * Returns the Cloud Monitoring metric providing the requested metric of the node.
22
+ *
23
+ * @returns The metric, or `undefined` if the node does not exist, has no resource, or the metric is not read from
24
+ * Cloud Monitoring for it.
25
+ */
26
+ findMetric() {
27
+ const node = this.findNode();
28
+ const resource = node?.node.data?.resource;
29
+ if (!node || typeof resource?.id !== 'string') {
30
+ return undefined;
31
+ }
32
+ return MONITORING_METRICS.find((m) => m.metric === this.metric &&
33
+ m.mapping.nodeType === node.type &&
34
+ m.mapping.resourceType === resource.type &&
35
+ (!m.measures || m.measures(node, this.graph)));
36
+ }
37
+ _supports() {
38
+ return !!this.findMetric();
39
+ }
40
+ }
@@ -0,0 +1,11 @@
1
+ import { GraphGetEnvironmentProvider, type GraphEnvironmentProvider } from '@causa/workspace-core';
2
+ /**
3
+ * Implements {@link GraphGetEnvironmentProvider} for Google Cloud.
4
+ * The fetchers read metrics and alerts from Cloud Monitoring, open error groups from Error Reporting as alerts, the
5
+ * state of Cloud Run services and Cloud Scheduler jobs, and build links to the Google Cloud console. Cloud Tasks
6
+ * queues, only known by a prefix of their name, are resolved by listing them.
7
+ */
8
+ export declare class GraphGetEnvironmentProviderForGoogle extends GraphGetEnvironmentProvider {
9
+ _call(): GraphEnvironmentProvider;
10
+ _supports(): boolean;
11
+ }
@@ -0,0 +1,20 @@
1
+ import { GraphGetEnvironmentProvider, } from '@causa/workspace-core';
2
+ import { CLOUD_TASKS_QUEUE_RESOLVER, GOOGLE_GRAPH_ENVIRONMENT_FETCHERS, GOOGLE_GRAPH_METRIC_DEFINITIONS, } from '../../graph/index.js';
3
+ /**
4
+ * Implements {@link GraphGetEnvironmentProvider} for Google Cloud.
5
+ * The fetchers read metrics and alerts from Cloud Monitoring, open error groups from Error Reporting as alerts, the
6
+ * state of Cloud Run services and Cloud Scheduler jobs, and build links to the Google Cloud console. Cloud Tasks
7
+ * queues, only known by a prefix of their name, are resolved by listing them.
8
+ */
9
+ export class GraphGetEnvironmentProviderForGoogle extends GraphGetEnvironmentProvider {
10
+ _call() {
11
+ return {
12
+ metrics: GOOGLE_GRAPH_METRIC_DEFINITIONS,
13
+ prefixResolvers: [CLOUD_TASKS_QUEUE_RESOLVER],
14
+ fetchers: GOOGLE_GRAPH_ENVIRONMENT_FETCHERS,
15
+ };
16
+ }
17
+ _supports() {
18
+ return true;
19
+ }
20
+ }
@@ -0,0 +1,3 @@
1
+ export { GraphFetchEnvironmentMetricSeriesForGoogle } from './fetch-environment-metric-series.js';
2
+ export { GraphGetEnvironmentProviderForGoogle } from './get-environment-provider.js';
3
+ export { GraphListRulesForGoogle } from './list-rules.js';
@@ -0,0 +1,3 @@
1
+ export { GraphFetchEnvironmentMetricSeriesForGoogle } from './fetch-environment-metric-series.js';
2
+ export { GraphGetEnvironmentProviderForGoogle } from './get-environment-provider.js';
3
+ export { GraphListRulesForGoogle } from './list-rules.js';
@@ -0,0 +1,11 @@
1
+ import { GraphListRules, type GraphRule } from '@causa/workspace-core';
2
+ /**
3
+ * Implements {@link GraphListRules} for Google Cloud.
4
+ * The rules read the Google-specific parts of the workspace configuration and model (Firestore collections, the
5
+ * entities projects access through their `google.*` outputs, triggers of Google-specific types), and mirror the
6
+ * resources created by the Causa Terraform modules for Google Cloud.
7
+ */
8
+ export declare class GraphListRulesForGoogle extends GraphListRules {
9
+ _call(): GraphRule[];
10
+ _supports(): boolean;
11
+ }
@@ -0,0 +1,16 @@
1
+ import { GraphListRules } from '@causa/workspace-core';
2
+ import { GOOGLE_GRAPH_RULES, GOOGLE_TERRAFORM_GRAPH_RULES, } from '../../graph/index.js';
3
+ /**
4
+ * Implements {@link GraphListRules} for Google Cloud.
5
+ * The rules read the Google-specific parts of the workspace configuration and model (Firestore collections, the
6
+ * entities projects access through their `google.*` outputs, triggers of Google-specific types), and mirror the
7
+ * resources created by the Causa Terraform modules for Google Cloud.
8
+ */
9
+ export class GraphListRulesForGoogle extends GraphListRules {
10
+ _call() {
11
+ return [...GOOGLE_GRAPH_RULES, ...GOOGLE_TERRAFORM_GRAPH_RULES];
12
+ }
13
+ _supports() {
14
+ return true;
15
+ }
16
+ }
@@ -8,10 +8,11 @@ import { GoogleIdentityPlatformGenerateCustomToken, GoogleIdentityPlatformGenera
8
8
  import { GooglePubSubWriteTopics } from './google-pubsub/index.js';
9
9
  import { GoogleServicesEnable } from './google-services/index.js';
10
10
  import { DatabaseQueryRecordsForSpanner, GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, ModelGenerateTypeScriptDecoratorsForGoogleSpanner, ModelSchemaExtractDatabaseForGoogleSpanner, } from './google-spanner/index.js';
11
+ import { GraphFetchEnvironmentMetricSeriesForGoogle, GraphGetEnvironmentProviderForGoogle, GraphListRulesForGoogle, } from './graph/index.js';
11
12
  import { ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, } from './project/index.js';
12
13
  import { SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, } from './secret/index.js';
13
14
  import { ServiceContainerQueryLogsForCloudRun } from './service-container/index.js';
14
15
  import { ModelGenerateTypeScriptTriggerDecoratorsForGoogle } from './typescript/index.js';
15
16
  export function registerFunctions(context) {
16
- 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, ModelGenerateTypeScriptTriggerDecoratorsForGoogle, ModelSchemaExtractDatabaseForGoogleFirestore, ModelSchemaExtractDatabaseForGoogleSpanner, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, ServiceContainerQueryLogsForCloudRun);
17
+ 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, GraphFetchEnvironmentMetricSeriesForGoogle, GraphGetEnvironmentProviderForGoogle, GraphListRulesForGoogle, ModelGenerateTypeScriptDecoratorsForGoogleFirestore, ModelGenerateTypeScriptDecoratorsForGoogleSpanner, ModelGenerateTypeScriptTriggerDecoratorsForGoogle, ModelSchemaExtractDatabaseForGoogleFirestore, ModelSchemaExtractDatabaseForGoogleSpanner, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, ServiceContainerQueryLogsForCloudRun);
17
18
  }