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

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 (54) hide show
  1. package/README.md +9 -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-firestore/generate-typescript-decorators.js +2 -1
  5. package/dist/functions/google-pubsub/index.d.ts +1 -1
  6. package/dist/functions/google-pubsub/index.js +1 -1
  7. package/dist/functions/google-pubsub/write-topics.d.ts +7 -0
  8. package/dist/functions/google-pubsub/write-topics.js +10 -1
  9. package/dist/functions/google-spanner/generate-typescript-decorators.js +2 -1
  10. package/dist/functions/google-spanner/list-databases.d.ts +1 -1
  11. package/dist/functions/graph/index.d.ts +1 -0
  12. package/dist/functions/graph/index.js +1 -0
  13. package/dist/functions/graph/list-rules.d.ts +11 -0
  14. package/dist/functions/graph/list-rules.js +16 -0
  15. package/dist/functions/index.js +2 -1
  16. package/dist/graph/cloud-run.d.ts +98 -0
  17. package/dist/graph/cloud-run.js +102 -0
  18. package/dist/graph/ids.d.ts +35 -0
  19. package/dist/graph/ids.js +44 -0
  20. package/dist/graph/index.d.ts +6 -0
  21. package/dist/graph/index.js +6 -0
  22. package/dist/graph/rules/configuration/enqueues-assumed-from-google-task-trigger.d.ts +6 -0
  23. package/dist/graph/rules/configuration/enqueues-assumed-from-google-task-trigger.js +39 -0
  24. package/dist/graph/rules/configuration/firestore.d.ts +11 -0
  25. package/dist/graph/rules/configuration/firestore.js +93 -0
  26. package/dist/graph/rules/configuration/index.d.ts +5 -0
  27. package/dist/graph/rules/configuration/index.js +11 -0
  28. package/dist/graph/rules/configuration/service-container.d.ts +13 -0
  29. package/dist/graph/rules/configuration/service-container.js +123 -0
  30. package/dist/graph/rules/terraform/api-router.d.ts +15 -0
  31. package/dist/graph/rules/terraform/api-router.js +117 -0
  32. package/dist/graph/rules/terraform/cloud-run-cron-triggers.d.ts +11 -0
  33. package/dist/graph/rules/terraform/cloud-run-cron-triggers.js +67 -0
  34. package/dist/graph/rules/terraform/cloud-run-grants.d.ts +16 -0
  35. package/dist/graph/rules/terraform/cloud-run-grants.js +118 -0
  36. package/dist/graph/rules/terraform/cloud-run-module.d.ts +6 -0
  37. package/dist/graph/rules/terraform/cloud-run-module.js +29 -0
  38. package/dist/graph/rules/terraform/cloud-run-pubsub-triggers.d.ts +14 -0
  39. package/dist/graph/rules/terraform/cloud-run-pubsub-triggers.js +103 -0
  40. package/dist/graph/rules/terraform/cloud-run-services.d.ts +12 -0
  41. package/dist/graph/rules/terraform/cloud-run-services.js +61 -0
  42. package/dist/graph/rules/terraform/cloud-run-tasks-triggers.d.ts +14 -0
  43. package/dist/graph/rules/terraform/cloud-run-tasks-triggers.js +97 -0
  44. package/dist/graph/rules/terraform/event-topics.d.ts +18 -0
  45. package/dist/graph/rules/terraform/event-topics.js +193 -0
  46. package/dist/graph/rules/terraform/index.d.ts +5 -0
  47. package/dist/graph/rules/terraform/index.js +13 -0
  48. package/dist/graph/rules/terraform/spanner-databases.d.ts +17 -0
  49. package/dist/graph/rules/terraform/spanner-databases.js +180 -0
  50. package/dist/graph/spanner.d.ts +106 -0
  51. package/dist/graph/spanner.js +118 -0
  52. package/dist/graph/terraform-modules.d.ts +87 -0
  53. package/dist/graph/terraform-modules.js +107 -0
  54. package/package.json +27 -26
@@ -0,0 +1,103 @@
1
+ import {} from '@causa/workspace-core';
2
+ import { brokerSubscriptionId, brokerTopicId, ModelFact, nodeId, triggerId, triggerLocator, } from '@causa/workspace-core/graph';
3
+ import { terraformDeploys } from '@causa/workspace-terraform';
4
+ import { CloudRunFact } from '../../cloud-run.js';
5
+ import { blockResource } from '../../terraform-modules.js';
6
+ /**
7
+ * A push `brokerSubscription` per Pub/Sub trigger of a project, for each Cloud Run module block deploying it with
8
+ * Pub/Sub triggers enabled. The locator is the trigger's. The module names the subscription `run-<service>-<trigger>`.
9
+ *
10
+ * Each subscription `realizes` its trigger, `targets` the service its module block deploys, and is delivered to by the
11
+ * broker topic of its trigger. A topic that no event schema of the workspace defines is created outside of it, and
12
+ * gets its own `brokerTopic` node.
13
+ *
14
+ * @param graph The context of the extraction.
15
+ * @returns The subscriptions, the external broker topics, and their edges.
16
+ */
17
+ export async function cloudRunPubSubTriggers(graph) {
18
+ const { locator } = graph;
19
+ const [cloudRun, model] = await Promise.all([
20
+ graph.get(CloudRunFact),
21
+ graph.get(ModelFact),
22
+ ]);
23
+ const nodes = [];
24
+ const edges = [];
25
+ const warnings = [];
26
+ const subscriptions = new Set();
27
+ for (const service of cloudRun) {
28
+ const source = service.block.declaration;
29
+ for (const [name, trigger] of service.pubsubTriggers) {
30
+ const subscriptionLocator = triggerLocator(service.project.directory, name);
31
+ const subscriptionId = brokerSubscriptionId(subscriptionLocator);
32
+ if (subscriptions.has(subscriptionLocator)) {
33
+ warnings.push({
34
+ message: `Trigger '${name}' of '${service.project.name}' is deployed with Pub/Sub triggers by several module blocks. A single subscription node stands for several subscriptions.`,
35
+ sources: [source],
36
+ });
37
+ }
38
+ subscriptions.add(subscriptionLocator);
39
+ const filter = trigger['google.pubSub']?.filter;
40
+ const subscription = `run-${service.name}-${name}`;
41
+ const data = {
42
+ filter: typeof filter === 'string' ? filter : undefined,
43
+ resource: await blockResource(graph, service.block, {
44
+ type: 'pubsub.googleapis.com/Subscription',
45
+ scope: service.project,
46
+ id: [
47
+ 'projects/',
48
+ service.projectPart,
49
+ `/subscriptions/${subscription}`,
50
+ ],
51
+ }, warnings),
52
+ };
53
+ nodes.push({
54
+ layer: 'infrastructure',
55
+ type: 'brokerSubscription',
56
+ locator: subscriptionLocator,
57
+ name: subscription,
58
+ sources: [
59
+ source,
60
+ await locator.configurationSource(service.project.context, [
61
+ 'serviceContainer',
62
+ 'triggers',
63
+ name,
64
+ ]),
65
+ ],
66
+ data,
67
+ });
68
+ edges.push(...terraformDeploys(service.block, nodeId('brokerSubscription', subscriptionLocator)), {
69
+ type: 'realizes',
70
+ from: subscriptionId,
71
+ to: triggerId(service.project.directory, name),
72
+ sources: [source],
73
+ }, {
74
+ type: 'targets',
75
+ from: subscriptionId,
76
+ to: service.id,
77
+ label: trigger.endpoint?.path,
78
+ sources: [source],
79
+ });
80
+ if (typeof trigger.topic !== 'string') {
81
+ continue;
82
+ }
83
+ const topicSource = await locator.configurationSource(service.project.context, ['serviceContainer', 'triggers', name, 'topic']);
84
+ edges.push({
85
+ type: 'delivers',
86
+ from: brokerTopicId(trigger.topic),
87
+ to: subscriptionId,
88
+ sources: [source, topicSource],
89
+ });
90
+ if (!model.topics.has(trigger.topic)) {
91
+ nodes.push({
92
+ layer: 'infrastructure',
93
+ type: 'brokerTopic',
94
+ locator: trigger.topic,
95
+ name: trigger.topic,
96
+ description: 'A topic no event schema of the workspace defines, created outside of it.',
97
+ sources: [topicSource],
98
+ });
99
+ }
100
+ }
101
+ }
102
+ return { nodes, edges, warnings };
103
+ }
@@ -0,0 +1,12 @@
1
+ import { type GraphRuleOutput } from '@causa/workspace-core';
2
+ import { type GraphContext } from '@causa/workspace-core/graph';
3
+ /**
4
+ * A `service` node per block of the Cloud Run module, and a `realizes` edge from it to the project whose configuration
5
+ * file the block reads. The service name is the block's `name` argument or the project name, and `platform` is the
6
+ * project's `serviceContainer.platform`. Blocks whose project cannot be found, and arguments that cannot be evaluated,
7
+ * are reported.
8
+ *
9
+ * @param graph The context of the extraction.
10
+ * @returns The services, and their edges.
11
+ */
12
+ export declare function cloudRunServices(graph: GraphContext): Promise<GraphRuleOutput>;
@@ -0,0 +1,61 @@
1
+ import {} from '@causa/workspace-core';
2
+ import { projectId, serviceLocator, } from '@causa/workspace-core/graph';
3
+ import { terraformDeploys } from '@causa/workspace-terraform';
4
+ import { CloudRunFact } from '../../cloud-run.js';
5
+ import { blockResource } from '../../terraform-modules.js';
6
+ /**
7
+ * A `service` node per block of the Cloud Run module, and a `realizes` edge from it to the project whose configuration
8
+ * file the block reads. The service name is the block's `name` argument or the project name, and `platform` is the
9
+ * project's `serviceContainer.platform`. Blocks whose project cannot be found, and arguments that cannot be evaluated,
10
+ * are reported.
11
+ *
12
+ * @param graph The context of the extraction.
13
+ * @returns The services, and their edges.
14
+ */
15
+ export async function cloudRunServices(graph) {
16
+ const { locator } = graph;
17
+ const cloudRun = await graph.get(CloudRunFact);
18
+ const nodes = [];
19
+ const edges = [];
20
+ const warnings = [];
21
+ for (const service of cloudRun) {
22
+ const source = service.block.declaration;
23
+ const data = {
24
+ platform: service.project.context
25
+ .asConfiguration()
26
+ .get('serviceContainer.platform'),
27
+ resource: await blockResource(graph, service.block, {
28
+ type: 'run.googleapis.com/Service',
29
+ scope: service.project,
30
+ id: [
31
+ 'projects/',
32
+ service.projectPart,
33
+ '/locations/',
34
+ service.locationPart,
35
+ `/services/${service.name}`,
36
+ ],
37
+ }, warnings),
38
+ };
39
+ nodes.push({
40
+ layer: 'infrastructure',
41
+ type: 'service',
42
+ locator: serviceLocator(service.project.directory, service.name),
43
+ name: service.name,
44
+ sources: [
45
+ source,
46
+ await locator.configurationSource(service.project.context, [
47
+ 'project',
48
+ 'name',
49
+ ]),
50
+ ],
51
+ data,
52
+ });
53
+ edges.push(...terraformDeploys(service.block, service.id), {
54
+ type: 'realizes',
55
+ from: service.id,
56
+ to: projectId(service.project.directory),
57
+ sources: [source],
58
+ });
59
+ }
60
+ return { nodes, edges, warnings };
61
+ }
@@ -0,0 +1,14 @@
1
+ import { type GraphRuleOutput } from '@causa/workspace-core';
2
+ import { type GraphContext } from '@causa/workspace-core/graph';
3
+ /**
4
+ * The resources a Cloud Run module block creates for the task triggers of a project, when tasks triggers are enabled:
5
+ *
6
+ * - A `queue` node per `queue` of the task triggers. The module suffixes the queue name with a random string, such that
7
+ * only a prefix of the resource name is known. The locator is the declared name. The queue `realizes` the task
8
+ * triggers declaring it, and `targets` the service the block deploys, which is the HTTP target of the tasks.
9
+ * - An `enqueues` edge from every service of the project to each queue.
10
+ *
11
+ * @param graph The context of the extraction.
12
+ * @returns The queues, and their edges.
13
+ */
14
+ export declare function cloudRunTasksTriggers(graph: GraphContext): Promise<GraphRuleOutput>;
@@ -0,0 +1,97 @@
1
+ import {} from '@causa/workspace-core';
2
+ import { nodeId, queueId, triggerId, } from '@causa/workspace-core/graph';
3
+ import { terraformDeploys } from '@causa/workspace-terraform';
4
+ import { CloudRunFact } from '../../cloud-run.js';
5
+ import { blockResource } from '../../terraform-modules.js';
6
+ /**
7
+ * The resources a Cloud Run module block creates for the task triggers of a project, when tasks triggers are enabled:
8
+ *
9
+ * - A `queue` node per `queue` of the task triggers. The module suffixes the queue name with a random string, such that
10
+ * only a prefix of the resource name is known. The locator is the declared name. The queue `realizes` the task
11
+ * triggers declaring it, and `targets` the service the block deploys, which is the HTTP target of the tasks.
12
+ * - An `enqueues` edge from every service of the project to each queue.
13
+ *
14
+ * @param graph The context of the extraction.
15
+ * @returns The queues, and their edges.
16
+ */
17
+ export async function cloudRunTasksTriggers(graph) {
18
+ const { locator } = graph;
19
+ const cloudRun = await graph.get(CloudRunFact);
20
+ const nodes = [];
21
+ const edges = [];
22
+ const warnings = [];
23
+ for (const service of cloudRun) {
24
+ const source = service.block.declaration;
25
+ for (const [name, trigger] of service.tasksTriggers) {
26
+ const queueSource = await locator.configurationSource(service.project.context, ['serviceContainer', 'triggers', name, 'queue']);
27
+ if (typeof trigger.queue !== 'string') {
28
+ warnings.push({
29
+ message: `Task trigger '${name}' has no queue.`,
30
+ sources: [queueSource],
31
+ });
32
+ continue;
33
+ }
34
+ const resource = await blockResource(graph, service.block, {
35
+ type: 'cloudtasks.googleapis.com/Queue',
36
+ scope: service.project,
37
+ prefix: true,
38
+ id: [
39
+ 'projects/',
40
+ service.projectPart,
41
+ '/locations/',
42
+ service.locationPart,
43
+ `/queues/${trigger.queue}-`,
44
+ ],
45
+ }, warnings);
46
+ nodes.push({
47
+ layer: 'infrastructure',
48
+ type: 'queue',
49
+ locator: trigger.queue,
50
+ name: trigger.queue,
51
+ sources: [source, queueSource],
52
+ data: { resource },
53
+ });
54
+ edges.push(...terraformDeploys(service.block, nodeId('queue', trigger.queue)), {
55
+ type: 'realizes',
56
+ from: queueId(trigger.queue),
57
+ to: triggerId(service.project.directory, name),
58
+ sources: [source],
59
+ }, {
60
+ type: 'targets',
61
+ from: queueId(trigger.queue),
62
+ to: service.id,
63
+ label: trigger.endpoint?.path,
64
+ sources: [source],
65
+ });
66
+ }
67
+ }
68
+ for (const services of Map.groupBy(cloudRun, (s) => s.project).values()) {
69
+ for (const granting of services.filter((s) => s.setTasksPermissions)) {
70
+ const queues = new Map();
71
+ for (const [name, trigger] of granting.tasksTriggers) {
72
+ if (typeof trigger.queue === 'string' && !queues.has(trigger.queue)) {
73
+ queues.set(trigger.queue, name);
74
+ }
75
+ }
76
+ for (const [queue, trigger] of queues) {
77
+ for (const service of services) {
78
+ edges.push({
79
+ type: 'enqueues',
80
+ from: service.id,
81
+ to: queueId(queue),
82
+ sources: [
83
+ granting.block.declaration,
84
+ await locator.configurationSource(granting.project.context, [
85
+ 'serviceContainer',
86
+ 'triggers',
87
+ trigger,
88
+ 'queue',
89
+ ]),
90
+ ],
91
+ });
92
+ }
93
+ }
94
+ }
95
+ }
96
+ return { nodes, edges, warnings };
97
+ }
@@ -0,0 +1,18 @@
1
+ import { type GraphRule } from '@causa/workspace-core';
2
+ /**
3
+ * The source of the Causa Terraform module creating the Pub/Sub topics, and the raw events BigQuery tables.
4
+ */
5
+ export declare const EVENT_TOPICS_MODULE_SOURCE = "causa-io/event-topics-pubsub/google";
6
+ /**
7
+ * The resources created by the event topics module block.
8
+ *
9
+ * - A `brokerTopic` node per event topic, named by the topic ID: the module creates one Pub/Sub topic per topic listed
10
+ * by `EventTopicList`. It `realizes` the event topic of the same ID.
11
+ * - When the block has a raw events dataset (`bigquery_raw_events_dataset`, or
12
+ * `google.pubSub.bigQueryStorage.rawEventsDatasetId`), a `database` node for the BigQuery dataset, and for each
13
+ * topic:
14
+ * - A `table` node, named by the topic ID with `.` and `-` replaced by `_`.
15
+ * - A BigQuery `brokerSubscription` node: the module creates one `bq-<topic>` subscription per topic, writing events
16
+ * to the topic's raw table. The broker topic `delivers` to it, and it `targets` the table.
17
+ */
18
+ export declare const eventTopicsModule: GraphRule;
@@ -0,0 +1,193 @@
1
+ import { GraphOriginKind, } from '@causa/workspace-core';
2
+ import { brokerSubscriptionId, brokerTopicId, databaseId, ModelFact, nodeId, tableId, tableLocator, topicId, } from '@causa/workspace-core/graph';
3
+ import { terraformDeploys } from '@causa/workspace-terraform';
4
+ import { relative } from 'path';
5
+ import { rawEventsTableName } from '../../../functions/google-pubsub/index.js';
6
+ import { BIGQUERY_ENGINE, bigQueryDatasetLocator, rawEventsSubscriptionLocator, } from '../../ids.js';
7
+ import { blockResource, infrastructureModules, argumentOrConfiguration, } from '../../terraform-modules.js';
8
+ /**
9
+ * The source of the Causa Terraform module creating the Pub/Sub topics, and the raw events BigQuery tables.
10
+ */
11
+ export const EVENT_TOPICS_MODULE_SOURCE = 'causa-io/event-topics-pubsub/google';
12
+ /**
13
+ * The configuration defining the raw events BigQuery dataset, when the block does not set it.
14
+ */
15
+ const RAW_EVENTS_DATASET_CONFIGURATION = 'google.pubSub.bigQueryStorage.rawEventsDatasetId';
16
+ /**
17
+ * Returns the raw events dataset of an event topics module block: its `bigquery_raw_events_dataset` argument, or the
18
+ * `google.pubSub.bigQueryStorage.rawEventsDatasetId` configuration.
19
+ *
20
+ * @param graph The context of the extraction.
21
+ * @param module The event topics module block.
22
+ * @param warnings The warnings of the rule, to which the one about a computed argument is added.
23
+ * @returns The dataset, or `undefined` if raw events are not stored.
24
+ */
25
+ async function rawEventsDataset(graph, module, warnings) {
26
+ const part = argumentOrConfiguration(module.block, 'bigquery_raw_events_dataset', RAW_EVENTS_DATASET_CONFIGURATION, warnings);
27
+ if (typeof part === 'string') {
28
+ return {
29
+ dataset: part,
30
+ part,
31
+ locator: bigQueryDatasetLocator(part),
32
+ sources: [],
33
+ };
34
+ }
35
+ const dataset = module.context
36
+ .asConfiguration()
37
+ .get(RAW_EVENTS_DATASET_CONFIGURATION);
38
+ if (typeof dataset !== 'string') {
39
+ return undefined;
40
+ }
41
+ return {
42
+ dataset,
43
+ part,
44
+ locator: bigQueryDatasetLocator(dataset),
45
+ sources: [
46
+ await graph.locator.configurationSource(module.context, RAW_EVENTS_DATASET_CONFIGURATION.split('.')),
47
+ ],
48
+ };
49
+ }
50
+ /**
51
+ * The resources created by the event topics module block.
52
+ *
53
+ * - A `brokerTopic` node per event topic, named by the topic ID: the module creates one Pub/Sub topic per topic listed
54
+ * by `EventTopicList`. It `realizes` the event topic of the same ID.
55
+ * - When the block has a raw events dataset (`bigquery_raw_events_dataset`, or
56
+ * `google.pubSub.bigQueryStorage.rawEventsDatasetId`), a `database` node for the BigQuery dataset, and for each
57
+ * topic:
58
+ * - A `table` node, named by the topic ID with `.` and `-` replaced by `_`.
59
+ * - A BigQuery `brokerSubscription` node: the module creates one `bq-<topic>` subscription per topic, writing events
60
+ * to the topic's raw table. The broker topic `delivers` to it, and it `targets` the table.
61
+ */
62
+ export const eventTopicsModule = {
63
+ name: 'eventTopicsModule',
64
+ kind: GraphOriginKind.Declared,
65
+ description: 'One `brokerTopic` per event topic for the first event topics module block, realizing the topic. When the block has a raw events BigQuery dataset, one `database` node for the dataset, and for each topic one `table` node, named by the topic ID with `.` and `-` replaced by `_`, and one `brokerSubscription` (`rawEvents/<topic>`) delivered to by the broker topic and targeting the table. Each resource is linked by a `deploys` edge from the projects applying the block.',
66
+ async run(graph) {
67
+ const { context } = graph;
68
+ const [eventTopics, model] = await Promise.all([
69
+ infrastructureModules(graph, EVENT_TOPICS_MODULE_SOURCE),
70
+ graph.get(ModelFact),
71
+ ]);
72
+ const nodes = [];
73
+ const edges = [];
74
+ const warnings = [];
75
+ const [module, ...skipped] = eventTopics;
76
+ if (!module) {
77
+ if (model.topics.size > 0) {
78
+ warnings.push({
79
+ message: 'There is no event topics module block: no broker topics are created for the event topics.',
80
+ });
81
+ }
82
+ return { warnings };
83
+ }
84
+ warnings.push(...skipped.map((m) => ({
85
+ message: 'The event topics module block is skipped: only the first one is processed, as several blocks would create resources with the same IDs.',
86
+ sources: [m.block.declaration],
87
+ })));
88
+ const source = module.block.declaration;
89
+ const projectPart = argumentOrConfiguration(module.block, 'gcp_project_id', 'google.project', warnings);
90
+ const rawEvents = await rawEventsDataset(graph, module, warnings);
91
+ if (rawEvents) {
92
+ const data = {
93
+ engine: BIGQUERY_ENGINE,
94
+ resource: await blockResource(graph, module.block, {
95
+ type: 'bigquery.googleapis.com/Dataset',
96
+ scope: module.project,
97
+ id: ['projects/', projectPart, '/datasets/', rawEvents.part],
98
+ }, warnings),
99
+ };
100
+ nodes.push({
101
+ layer: 'infrastructure',
102
+ type: 'database',
103
+ locator: rawEvents.locator,
104
+ name: rawEvents.dataset,
105
+ description: 'Raw events from Pub/Sub, one table per topic.',
106
+ sources: [source, ...rawEvents.sources],
107
+ data,
108
+ });
109
+ edges.push(...terraformDeploys(module.block, nodeId('database', rawEvents.locator)));
110
+ }
111
+ for (const topic of model.topics.values()) {
112
+ const schemaSource = {
113
+ path: relative(context.rootPath, topic.schemaFilePath),
114
+ };
115
+ nodes.push({
116
+ layer: 'infrastructure',
117
+ type: 'brokerTopic',
118
+ locator: topic.id,
119
+ name: topic.id,
120
+ sources: [source, schemaSource],
121
+ data: {
122
+ resource: await blockResource(graph, module.block, {
123
+ type: 'pubsub.googleapis.com/Topic',
124
+ scope: module.project,
125
+ id: ['projects/', projectPart, `/topics/${topic.id}`],
126
+ }, warnings),
127
+ },
128
+ });
129
+ edges.push(...terraformDeploys(module.block, nodeId('brokerTopic', topic.id)), {
130
+ type: 'realizes',
131
+ from: brokerTopicId(topic.id),
132
+ to: topicId(topic.id),
133
+ sources: [source],
134
+ });
135
+ if (!rawEvents) {
136
+ continue;
137
+ }
138
+ const tableName = rawEventsTableName(topic.id);
139
+ const rawTableLocator = tableLocator(rawEvents.locator, tableName);
140
+ nodes.push({
141
+ layer: 'infrastructure',
142
+ type: 'table',
143
+ locator: rawTableLocator,
144
+ parent: databaseId(rawEvents.locator),
145
+ name: tableName,
146
+ description: `Raw \`${topic.id}\` events.`,
147
+ sources: [source, schemaSource],
148
+ data: {
149
+ resource: await blockResource(graph, module.block, {
150
+ type: 'bigquery.googleapis.com/Table',
151
+ scope: module.project,
152
+ id: [
153
+ 'projects/',
154
+ projectPart,
155
+ '/datasets/',
156
+ rawEvents.part,
157
+ `/tables/${tableName}`,
158
+ ],
159
+ }, warnings),
160
+ },
161
+ });
162
+ edges.push(...terraformDeploys(module.block, nodeId('table', rawTableLocator)));
163
+ const subscriptionLocator = rawEventsSubscriptionLocator(topic.id);
164
+ const subscription = `bq-${topic.id}`;
165
+ nodes.push({
166
+ layer: 'infrastructure',
167
+ type: 'brokerSubscription',
168
+ locator: subscriptionLocator,
169
+ name: subscription,
170
+ sources: [source, ...rawEvents.sources],
171
+ data: {
172
+ resource: await blockResource(graph, module.block, {
173
+ type: 'pubsub.googleapis.com/Subscription',
174
+ scope: module.project,
175
+ id: ['projects/', projectPart, `/subscriptions/${subscription}`],
176
+ }, warnings),
177
+ },
178
+ });
179
+ edges.push(...terraformDeploys(module.block, nodeId('brokerSubscription', subscriptionLocator)), {
180
+ type: 'delivers',
181
+ from: brokerTopicId(topic.id),
182
+ to: brokerSubscriptionId(subscriptionLocator),
183
+ sources: [source],
184
+ }, {
185
+ type: 'targets',
186
+ from: brokerSubscriptionId(subscriptionLocator),
187
+ to: tableId(rawEvents.locator, tableName),
188
+ sources: [source],
189
+ });
190
+ }
191
+ return { nodes, edges, warnings };
192
+ },
193
+ };
@@ -0,0 +1,5 @@
1
+ import type { GraphRule } from '@causa/workspace-core';
2
+ /**
3
+ * The Google rules mirroring the Causa Terraform modules, grouped by module.
4
+ */
5
+ export declare const GOOGLE_TERRAFORM_GRAPH_RULES: readonly GraphRule[];
@@ -0,0 +1,13 @@
1
+ import { apiRouterModule } from './api-router.js';
2
+ import { cloudRunModule } from './cloud-run-module.js';
3
+ import { eventTopicsModule } from './event-topics.js';
4
+ import { spannerDatabasesModule } from './spanner-databases.js';
5
+ /**
6
+ * The Google rules mirroring the Causa Terraform modules, grouped by module.
7
+ */
8
+ export const GOOGLE_TERRAFORM_GRAPH_RULES = [
9
+ cloudRunModule,
10
+ apiRouterModule,
11
+ eventTopicsModule,
12
+ spannerDatabasesModule,
13
+ ];
@@ -0,0 +1,17 @@
1
+ import { type GraphRule } from '@causa/workspace-core';
2
+ /**
3
+ * The source of the Causa Terraform module creating the Spanner instance and databases.
4
+ */
5
+ export declare const SPANNER_DATABASES_MODULE_SOURCE = "causa-io/spanner-databases/google";
6
+ /**
7
+ * The resources created by the blocks of the Spanner databases module:
8
+ *
9
+ * - A `google.spanner.instance` node per block, which creates the instance from `google.spanner.instance.name`.
10
+ * - A `database` node per Spanner database, as `GoogleSpannerListDatabases` lists them. The locator is
11
+ * `google.spanner/<instance>.<database>`, and the parent is the instance.
12
+ * - A `table` node per `CREATE TABLE` statement in the DDL files of each database. Tables no schema declares (e.g. an
13
+ * outbox) are still nodes, realizing nothing.
14
+ * - A `realizes` edge from a table to the entity whose binding names it. The table is the one created by the DDL of a
15
+ * database, disambiguated by domain when several databases create a table with the same name.
16
+ */
17
+ export declare const spannerDatabasesModule: GraphRule;