@causa/workspace-google 0.9.3 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -6
- package/dist/backfilling/pubsub.js +2 -2
- package/dist/code-generation/google-firestore-renderer.d.ts +12 -0
- package/dist/code-generation/google-firestore-renderer.js +90 -0
- package/dist/code-generation/google-spanner-renderer.d.ts +4 -4
- package/dist/code-generation/google-spanner-renderer.js +52 -45
- package/dist/code-generation/index.d.ts +1 -0
- package/dist/code-generation/index.js +1 -0
- package/dist/code-generation/utils.d.ts +12 -0
- package/dist/code-generation/utils.js +21 -0
- package/dist/functions/index.js +2 -2
- package/dist/functions/typescript/get-decorator-renderer-google-firestore.d.ts +9 -0
- package/dist/functions/typescript/get-decorator-renderer-google-firestore.js +15 -0
- package/dist/functions/typescript/get-decorator-renderer-google-spanner.d.ts +2 -3
- package/dist/functions/typescript/get-decorator-renderer-google-spanner.js +3 -10
- package/dist/functions/typescript/index.d.ts +1 -0
- package/dist/functions/typescript/index.js +1 -0
- package/dist/services/cloud-run-pubsub-trigger.js +3 -1
- package/dist/services/cloud-run.js +4 -5
- package/dist/services/resource-manager.js +2 -6
- package/package.json +40 -35
package/README.md
CHANGED
|
@@ -85,28 +85,72 @@ secrets:
|
|
|
85
85
|
|
|
86
86
|
### Code generation
|
|
87
87
|
|
|
88
|
-
This module
|
|
88
|
+
This module provides TypeScript decorator renderers for Spanner and Firestore, which can be used to add `@SpannerTable`, `@SpannerColumn`, `@FirestoreCollection`, and `@SoftDeletedFirestoreCollection` decorators to classes generated from events. Below is an example of how to enable it for a JSONSchema object:
|
|
89
89
|
|
|
90
90
|
```yaml
|
|
91
|
-
title:
|
|
91
|
+
title: MySpannerTable
|
|
92
92
|
type: object
|
|
93
93
|
additionalProperties: false
|
|
94
94
|
causa:
|
|
95
95
|
# This must be set for the decorators to be added to both the class and its properties.
|
|
96
96
|
# The content of the object will be passed as the argument to the `@SpannerTable` decorator.
|
|
97
|
-
|
|
97
|
+
googleSpannerTable:
|
|
98
98
|
primaryKey: [id]
|
|
99
99
|
properties:
|
|
100
100
|
id:
|
|
101
101
|
type: string
|
|
102
102
|
format: uuid
|
|
103
|
-
# In most cases, the property-level `
|
|
104
|
-
# If needed, the content of `
|
|
103
|
+
# In most cases, the property-level `googleSpannerColumn` attribute does not need to be set. The decorator configuration will be automatically inferred.
|
|
104
|
+
# If needed, the content of `googleSpannerColumn` will be passed as the argument to the `@SpannerColumn` decorator.
|
|
105
105
|
# causa:
|
|
106
|
-
#
|
|
106
|
+
# googleSpannerColumn:
|
|
107
107
|
# isJson: false
|
|
108
108
|
myProperty:
|
|
109
109
|
type: string
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
title: MyFirestoreDocument
|
|
113
|
+
type: object
|
|
114
|
+
additionalProperties: false
|
|
115
|
+
causa:
|
|
116
|
+
# This must be set for the decorators to be added to the class.
|
|
117
|
+
googleFirestoreCollection:
|
|
118
|
+
# Mandatory, the name of the Firestore collection.
|
|
119
|
+
name: myCollection
|
|
120
|
+
# Mandatory, determines how to create the path for a document.
|
|
121
|
+
path: [property: id]
|
|
122
|
+
# This could also contain plain strings, e.g. for `{id}/subCollection/{otherProp}`:
|
|
123
|
+
# path: [property: id, subCollection, property: otherProp]
|
|
124
|
+
# Optional, adds the `@SoftDeletedFirestoreCollection` decorator.
|
|
125
|
+
hasSoftDelete: true
|
|
126
|
+
properties:
|
|
127
|
+
id:
|
|
128
|
+
type: string
|
|
129
|
+
otherProp:
|
|
130
|
+
type: string
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
To restrict the decorator to some schema files, you can configure the parent `typescriptModelClass` generator:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
model:
|
|
137
|
+
codeGenerators:
|
|
138
|
+
- generator: typescriptModelClass
|
|
139
|
+
|
|
140
|
+
# ...Rest of the configuration...
|
|
141
|
+
|
|
142
|
+
google:
|
|
143
|
+
spanner:
|
|
144
|
+
# Decorators will only be added to the schemas in those files, relative to the project directory.
|
|
145
|
+
globs:
|
|
146
|
+
- ../entities/*.yaml
|
|
147
|
+
# The name of the property / column to which the `softDelete` option should be added.
|
|
148
|
+
softDeletionColumn: deletedAt
|
|
149
|
+
|
|
150
|
+
firestore:
|
|
151
|
+
# Same as Spanner globs.
|
|
152
|
+
globs:
|
|
153
|
+
- ../firestore/*.yaml
|
|
110
154
|
```
|
|
111
155
|
|
|
112
156
|
## 🔨 Custom `google` commands
|
|
@@ -7,8 +7,8 @@ import { PubSubService } from '../services/index.js';
|
|
|
7
7
|
*/
|
|
8
8
|
const PUBLISH_OPTIONS = {
|
|
9
9
|
flowControlOptions: {
|
|
10
|
-
maxOutstandingBytes:
|
|
11
|
-
maxOutstandingMessages:
|
|
10
|
+
maxOutstandingBytes: 10 * 1024 * 1024,
|
|
11
|
+
maxOutstandingMessages: 1000,
|
|
12
12
|
},
|
|
13
13
|
batching: {
|
|
14
14
|
maxBytes: 10 * 1024 * 1024,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ClassContext, type TypeScriptDecorator, TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
2
|
+
/**
|
|
3
|
+
* A {@link TypeScriptDecoratorsRenderer} that adds Google Firestore decorators from the Causa Google runtime.
|
|
4
|
+
*
|
|
5
|
+
* If an object schema is marked with the `googleFirestoreCollection` attribute, the `@FirestoreCollection` decorator
|
|
6
|
+
* is added to the class. If the `hasSoftDelete` property is `true`, the `@SoftDeletedFirestoreCollection` decorator
|
|
7
|
+
* is also added.
|
|
8
|
+
*/
|
|
9
|
+
export declare class GoogleFirestoreRenderer extends TypeScriptWithDecoratorsRenderer {
|
|
10
|
+
decoratorsForClass(context: ClassContext): TypeScriptDecorator[];
|
|
11
|
+
decoratorsForProperty(): TypeScriptDecorator[];
|
|
12
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { TypeScriptWithDecoratorsRenderer, } from '@causa/workspace-typescript';
|
|
2
|
+
import { Name, panic } from 'quicktype-core';
|
|
3
|
+
import { schemaMatchesGlobPatterns } from './utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* The name of the Causa attribute that should be present for a class to be decorated with Google Firestore decorators.
|
|
6
|
+
*/
|
|
7
|
+
const GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE = 'googleFirestoreCollection';
|
|
8
|
+
/**
|
|
9
|
+
* The name of the Causa module for the TypeScript Google runtime.
|
|
10
|
+
*/
|
|
11
|
+
const CAUSA_GOOGLE_MODULE = '@causa/runtime-google';
|
|
12
|
+
/**
|
|
13
|
+
* A {@link TypeScriptDecoratorsRenderer} that adds Google Firestore decorators from the Causa Google runtime.
|
|
14
|
+
*
|
|
15
|
+
* If an object schema is marked with the `googleFirestoreCollection` attribute, the `@FirestoreCollection` decorator
|
|
16
|
+
* is added to the class. If the `hasSoftDelete` property is `true`, the `@SoftDeletedFirestoreCollection` decorator
|
|
17
|
+
* is also added.
|
|
18
|
+
*/
|
|
19
|
+
export class GoogleFirestoreRenderer extends TypeScriptWithDecoratorsRenderer {
|
|
20
|
+
decoratorsForClass(context) {
|
|
21
|
+
const collectionAttribute = context.objectAttributes[GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE];
|
|
22
|
+
const globs = this.targetLanguage.options.generatorOptions?.google?.firestore?.globs;
|
|
23
|
+
if (!collectionAttribute ||
|
|
24
|
+
!schemaMatchesGlobPatterns(this, context, globs)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const debugName = context.classType.getCombinedName();
|
|
28
|
+
if (typeof collectionAttribute !== 'object') {
|
|
29
|
+
panic(`Invalid '${GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE}' attribute on '${debugName}'. Expected an object.`);
|
|
30
|
+
}
|
|
31
|
+
const { name, path, hasSoftDelete } = collectionAttribute;
|
|
32
|
+
if (typeof name !== 'string') {
|
|
33
|
+
panic(`Invalid '${GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE}' attribute on '${debugName}'. Expected an object with a 'name' string property.`);
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(path)) {
|
|
36
|
+
panic(`Invalid '${GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE}' attribute on '${debugName}'. Expected an object with a 'path' array property.`);
|
|
37
|
+
}
|
|
38
|
+
const requiredProperties = path.flatMap((e) => typeof e === 'object' &&
|
|
39
|
+
e !== null &&
|
|
40
|
+
'property' in e &&
|
|
41
|
+
typeof e.property === 'string'
|
|
42
|
+
? [e.property]
|
|
43
|
+
: []);
|
|
44
|
+
const propertyReferences = new Map();
|
|
45
|
+
this.forEachClassProperty(context.classType, 'none', (name, jsonName) => {
|
|
46
|
+
if (requiredProperties.includes(jsonName)) {
|
|
47
|
+
propertyReferences.set(jsonName, name);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
for (const propertyName of requiredProperties) {
|
|
51
|
+
if (!propertyReferences.has(propertyName)) {
|
|
52
|
+
panic(`Property '${propertyName}' referenced in 'path' not found in '${debugName}'.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const elements = path.map((element) => {
|
|
56
|
+
if (typeof element === 'string') {
|
|
57
|
+
return JSON.stringify(element);
|
|
58
|
+
}
|
|
59
|
+
if (typeof element === 'object' &&
|
|
60
|
+
element !== null &&
|
|
61
|
+
'property' in element) {
|
|
62
|
+
const propertyName = element.property;
|
|
63
|
+
const generatedName = propertyReferences.get(propertyName);
|
|
64
|
+
if (!generatedName) {
|
|
65
|
+
panic(`Property '${propertyName}' not found in property references.`);
|
|
66
|
+
}
|
|
67
|
+
return ['doc.', generatedName];
|
|
68
|
+
}
|
|
69
|
+
panic(`Invalid path element in '${GOOGLE_FIRESTORE_COLLECTION_ATTRIBUTE}' attribute on '${debugName}'.`);
|
|
70
|
+
});
|
|
71
|
+
const pathExpression = path.length === 1
|
|
72
|
+
? elements
|
|
73
|
+
: ['[', ...elements.flatMap((e) => [e, ', ']), "].join('/')"];
|
|
74
|
+
const decorators = [];
|
|
75
|
+
this.addDecoratorToList(decorators, context, 'FirestoreCollection', CAUSA_GOOGLE_MODULE, [
|
|
76
|
+
'@FirestoreCollection({ name: ',
|
|
77
|
+
JSON.stringify(name),
|
|
78
|
+
', path: (doc) => ',
|
|
79
|
+
...pathExpression,
|
|
80
|
+
'})',
|
|
81
|
+
]);
|
|
82
|
+
if (hasSoftDelete === true) {
|
|
83
|
+
this.addDecoratorToList(decorators, context, 'SoftDeletedFirestoreCollection', CAUSA_GOOGLE_MODULE, ['@SoftDeletedFirestoreCollection()']);
|
|
84
|
+
}
|
|
85
|
+
return decorators;
|
|
86
|
+
}
|
|
87
|
+
decoratorsForProperty() {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type ClassContext, type ClassPropertyContext, type TypeScriptDecorator,
|
|
1
|
+
import { type ClassContext, type ClassPropertyContext, type TypeScriptDecorator, TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
2
2
|
/**
|
|
3
3
|
* A {@link TypeScriptDecoratorsRenderer} that adds Google Spanner decorators from the Causa Google runtime.
|
|
4
4
|
*
|
|
5
|
-
* If an object schema is marked with the `
|
|
6
|
-
*
|
|
5
|
+
* If an object schema is marked with the `googleSpannerTable` attribute, the `@SpannerTable` decorator is added to the
|
|
6
|
+
* class, and `@SpannerColumn` decorators are added to all its properties.
|
|
7
7
|
*/
|
|
8
|
-
export declare class GoogleSpannerRenderer extends
|
|
8
|
+
export declare class GoogleSpannerRenderer extends TypeScriptWithDecoratorsRenderer {
|
|
9
9
|
decoratorsForClass(context: ClassContext): TypeScriptDecorator[];
|
|
10
10
|
decoratorsForProperty(context: ClassPropertyContext): TypeScriptDecorator[];
|
|
11
11
|
}
|
|
@@ -1,24 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TypeScriptWithDecoratorsRenderer, getSingleType, typeScriptSourceForObject, } from '@causa/workspace-typescript';
|
|
2
2
|
import { panic } from 'quicktype-core';
|
|
3
|
+
import { schemaMatchesGlobPatterns } from './utils.js';
|
|
3
4
|
/**
|
|
4
5
|
* The name of the Causa attribute that should be present for a class to be decorated with Google Spanner decorators.
|
|
5
6
|
*/
|
|
6
|
-
const GOOGLE_SPANNER_TABLE_ATTRIBUTE = '
|
|
7
|
-
/**
|
|
8
|
-
* The name of the Causa attribute that can be present on a class to only generate `@SpannerColumn` decorators for its
|
|
9
|
-
* properties, but not a `@SpannerTable` decorator for the class itself.
|
|
10
|
-
*/
|
|
11
|
-
const GOOGLE_SPANNER_NESTED_TYPE_ATTRIBUTE = 'tsGoogleSpannerNestedType';
|
|
7
|
+
const GOOGLE_SPANNER_TABLE_ATTRIBUTE = 'googleSpannerTable';
|
|
12
8
|
/**
|
|
13
9
|
* The name of the optional Causa attribute that can be present on an object property schema to specify options for the
|
|
14
10
|
* `@SpannerColumn` decorator.
|
|
15
11
|
*/
|
|
16
|
-
const GOOGLE_SPANNER_COLUMN_ATTRIBUTE = '
|
|
17
|
-
/**
|
|
18
|
-
* The name of the `decoratorOptions` key that can be used to specify the name of the property that should be used as
|
|
19
|
-
* the soft deletion column.
|
|
20
|
-
*/
|
|
21
|
-
const GOOGLE_SPANNER_SOFT_DELETION_COLUMN_OPTION = 'googleSpannerSoftDeletionColumn';
|
|
12
|
+
const GOOGLE_SPANNER_COLUMN_ATTRIBUTE = 'googleSpannerColumn';
|
|
22
13
|
/**
|
|
23
14
|
* The name of the Causa module for the TypeScript Google runtime.
|
|
24
15
|
*/
|
|
@@ -28,8 +19,6 @@ const CAUSA_GOOGLE_MODULE = '@causa/runtime-google';
|
|
|
28
19
|
* specifies the type of the column. In this case, the renderer should not infer any type information.
|
|
29
20
|
*/
|
|
30
21
|
const TYPE_INFO_COLUMN_ATTRIBUTE_NAMES = [
|
|
31
|
-
'nestedType',
|
|
32
|
-
'nullifyNested',
|
|
33
22
|
'isBigInt',
|
|
34
23
|
'isInt',
|
|
35
24
|
'isPreciseDate',
|
|
@@ -38,55 +27,73 @@ const TYPE_INFO_COLUMN_ATTRIBUTE_NAMES = [
|
|
|
38
27
|
/**
|
|
39
28
|
* A {@link TypeScriptDecoratorsRenderer} that adds Google Spanner decorators from the Causa Google runtime.
|
|
40
29
|
*
|
|
41
|
-
* If an object schema is marked with the `
|
|
42
|
-
*
|
|
30
|
+
* If an object schema is marked with the `googleSpannerTable` attribute, the `@SpannerTable` decorator is added to the
|
|
31
|
+
* class, and `@SpannerColumn` decorators are added to all its properties.
|
|
43
32
|
*/
|
|
44
|
-
export class GoogleSpannerRenderer extends
|
|
33
|
+
export class GoogleSpannerRenderer extends TypeScriptWithDecoratorsRenderer {
|
|
45
34
|
decoratorsForClass(context) {
|
|
46
35
|
const tableAttribute = context.objectAttributes[GOOGLE_SPANNER_TABLE_ATTRIBUTE];
|
|
47
|
-
|
|
36
|
+
const globs = this.targetLanguage.options.generatorOptions?.google?.spanner?.globs;
|
|
37
|
+
if (!tableAttribute || !schemaMatchesGlobPatterns(this, context, globs)) {
|
|
48
38
|
return [];
|
|
49
39
|
}
|
|
50
40
|
if (typeof tableAttribute !== 'object' ||
|
|
51
41
|
!('primaryKey' in tableAttribute) ||
|
|
52
|
-
!Array.isArray(tableAttribute.primaryKey)
|
|
53
|
-
|
|
42
|
+
!Array.isArray(tableAttribute.primaryKey) ||
|
|
43
|
+
tableAttribute.primaryKey.length === 0 ||
|
|
44
|
+
tableAttribute.primaryKey.some((k) => typeof k !== 'string')) {
|
|
45
|
+
panic(`Invalid '${GOOGLE_SPANNER_TABLE_ATTRIBUTE}' attribute on '${context.classType.getCombinedName()}'. Expected an object with a 'primaryKey' array.`);
|
|
46
|
+
}
|
|
47
|
+
if ('name' in tableAttribute && typeof tableAttribute.name !== 'string') {
|
|
48
|
+
panic(`Invalid 'name' in '${GOOGLE_SPANNER_TABLE_ATTRIBUTE}' attribute on '${context.classType.getCombinedName()}'. Expected a string.`);
|
|
54
49
|
}
|
|
55
|
-
const optionsSource = typeScriptSourceForObject(
|
|
50
|
+
const optionsSource = typeScriptSourceForObject({
|
|
51
|
+
primaryKey: tableAttribute.primaryKey,
|
|
52
|
+
name: tableAttribute.name,
|
|
53
|
+
});
|
|
56
54
|
const decorators = [];
|
|
57
55
|
this.addDecoratorToList(decorators, context, 'SpannerTable', CAUSA_GOOGLE_MODULE, ['@SpannerTable(', optionsSource, ')']);
|
|
58
56
|
return decorators;
|
|
59
57
|
}
|
|
60
58
|
decoratorsForProperty(context) {
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
const globs = this.targetLanguage.options.generatorOptions?.google?.spanner?.globs;
|
|
60
|
+
if (!context.objectAttributes[GOOGLE_SPANNER_TABLE_ATTRIBUTE] ||
|
|
61
|
+
!schemaMatchesGlobPatterns(this, context, globs)) {
|
|
63
62
|
return [];
|
|
64
63
|
}
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const softDeletionColumn =
|
|
68
|
-
|
|
64
|
+
const attributes = context.propertyAttributes[GOOGLE_SPANNER_COLUMN_ATTRIBUTE] ?? {};
|
|
65
|
+
const { generatorOptions } = this.targetLanguage.options;
|
|
66
|
+
const softDeletionColumn = generatorOptions?.google?.spanner?.softDeletionColumn;
|
|
67
|
+
const { name: overriddenName, tsOptions } = attributes;
|
|
68
|
+
if (tsOptions && typeof tsOptions !== 'object') {
|
|
69
|
+
panic(`Invalid 'tsOptions' in '${GOOGLE_SPANNER_COLUMN_ATTRIBUTE}' attribute. Expected an object.`);
|
|
70
|
+
}
|
|
71
|
+
const columnAttributes = tsOptions ?? {};
|
|
72
|
+
if (overriddenName) {
|
|
73
|
+
if (typeof overriddenName !== 'string') {
|
|
74
|
+
panic(`Invalid 'name' in '${GOOGLE_SPANNER_COLUMN_ATTRIBUTE}' attribute. Expected a string.`);
|
|
75
|
+
}
|
|
76
|
+
columnAttributes.name = overriddenName;
|
|
77
|
+
}
|
|
78
|
+
const columnName = overriddenName ?? this.names.get(context.name);
|
|
79
|
+
if (columnName === softDeletionColumn) {
|
|
69
80
|
columnAttributes.softDelete = true;
|
|
70
81
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
82
|
+
const singleTypeInfo = getSingleType(context.property.type);
|
|
83
|
+
const schemaOverridesTypeInfo = TYPE_INFO_COLUMN_ATTRIBUTE_NAMES.some((name) => name in columnAttributes);
|
|
84
|
+
if (!schemaOverridesTypeInfo && singleTypeInfo) {
|
|
85
|
+
switch (singleTypeInfo.type.kind) {
|
|
86
|
+
case 'class':
|
|
87
|
+
case 'object':
|
|
88
|
+
case 'map':
|
|
89
|
+
columnAttributes.isJson = true;
|
|
90
|
+
break;
|
|
91
|
+
case 'integer':
|
|
92
|
+
columnAttributes.isInt = true;
|
|
93
|
+
break;
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
|
-
const optionsSource = typeScriptSourceForObject(columnAttributes
|
|
88
|
-
encoder: (key, value) => key === 'nestedType' ? value : JSON.stringify(value),
|
|
89
|
-
});
|
|
96
|
+
const optionsSource = typeScriptSourceForObject(columnAttributes);
|
|
90
97
|
const decorators = [];
|
|
91
98
|
this.addDecoratorToList(decorators, context, 'SpannerColumn', CAUSA_GOOGLE_MODULE, ['@SpannerColumn(', optionsSource, ')']);
|
|
92
99
|
return decorators;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ClassContext } from '@causa/workspace-typescript';
|
|
2
|
+
import { TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Checks whether the schema matches the glob patterns specified in the renderer's options.
|
|
5
|
+
* If this returns `false`, the decorators should not be generated for the schema.
|
|
6
|
+
*
|
|
7
|
+
* @param renderer The renderer instance.
|
|
8
|
+
* @param context The {@link ClassContext} of the schema to check.
|
|
9
|
+
* @param globs The glob patterns to match against, or undefined/null if not configured.
|
|
10
|
+
* @returns `true` if the decorators should be generated for the schema.
|
|
11
|
+
*/
|
|
12
|
+
export declare function schemaMatchesGlobPatterns(renderer: TypeScriptWithDecoratorsRenderer, context: ClassContext, globs: unknown): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
2
|
+
import micromatch from 'micromatch';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Checks whether the schema matches the glob patterns specified in the renderer's options.
|
|
6
|
+
* If this returns `false`, the decorators should not be generated for the schema.
|
|
7
|
+
*
|
|
8
|
+
* @param renderer The renderer instance.
|
|
9
|
+
* @param context The {@link ClassContext} of the schema to check.
|
|
10
|
+
* @param globs The glob patterns to match against, or undefined/null if not configured.
|
|
11
|
+
* @returns `true` if the decorators should be generated for the schema.
|
|
12
|
+
*/
|
|
13
|
+
export function schemaMatchesGlobPatterns(renderer, context, globs) {
|
|
14
|
+
const { uri } = context;
|
|
15
|
+
if (!uri || !globs || !Array.isArray(globs)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
const projectPath = renderer.targetLanguage.workspaceContext.getProjectPathOrThrow();
|
|
19
|
+
const absoluteGlobs = globs.map((g) => join(projectPath, g));
|
|
20
|
+
return micromatch.isMatch(uri, absoluteGlobs);
|
|
21
|
+
}
|
package/dist/functions/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { GoogleServicesEnable } from './google-services/index.js';
|
|
|
9
9
|
import { GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, } from './google-spanner/index.js';
|
|
10
10
|
import { ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, } from './project/index.js';
|
|
11
11
|
import { SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, } from './secret/index.js';
|
|
12
|
-
import { TypeScriptGetDecoratorRendererForGoogleSpanner } from './typescript/index.js';
|
|
12
|
+
import { TypeScriptGetDecoratorRendererForGoogleFirestore, TypeScriptGetDecoratorRendererForGoogleSpanner, } from './typescript/index.js';
|
|
13
13
|
export function registerFunctions(context) {
|
|
14
|
-
context.registerFunctionImplementations(EmulatorStartForFirebaseStorage, EmulatorStartForFirestore, EmulatorStartForIdentityPlatform, EmulatorStartForPubSub, EmulatorStartForSpanner, EmulatorStopForFirebaseStorage, EmulatorStopForFirestore, EmulatorStopForIdentityPlatform, EmulatorStopForPubSub, EmulatorStopForSpanner, EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, GoogleAppCheckGenerateToken, GoogleFirebaseStorageMergeRules, GoogleFirestoreMergeRules, GoogleIdentityPlatformGenerateCustomToken, GoogleIdentityPlatformGenerateToken, GooglePubSubWriteTopics, GoogleServicesEnable, GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, TypeScriptGetDecoratorRendererForGoogleSpanner);
|
|
14
|
+
context.registerFunctionImplementations(EmulatorStartForFirebaseStorage, EmulatorStartForFirestore, EmulatorStartForIdentityPlatform, EmulatorStartForPubSub, EmulatorStartForSpanner, EmulatorStopForFirebaseStorage, EmulatorStopForFirestore, EmulatorStopForIdentityPlatform, EmulatorStopForPubSub, EmulatorStopForSpanner, EventTopicBrokerCreateTopicForPubSub, EventTopicBrokerCreateTriggerForCloudRun, EventTopicBrokerDeleteTopicForPubSub, EventTopicBrokerDeleteTriggerResourceForCloudRunInvokerRole, EventTopicBrokerDeleteTriggerResourceForPubSubSubscription, EventTopicBrokerDeleteTriggerResourceForServiceAccount, EventTopicBrokerGetTopicIdForPubSub, EventTopicBrokerPublishEventsForGoogle, GoogleAppCheckGenerateToken, GoogleFirebaseStorageMergeRules, GoogleFirestoreMergeRules, GoogleIdentityPlatformGenerateCustomToken, GoogleIdentityPlatformGenerateToken, GooglePubSubWriteTopics, GoogleServicesEnable, GoogleSpannerListDatabases, GoogleSpannerWriteDatabases, ProjectGetArtefactDestinationForCloudFunctions, ProjectGetArtefactDestinationForCloudRun, ProjectPushArtefactForCloudFunctions, SecretFetchForGoogleAccessToken, SecretFetchForGoogleSecretManager, TypeScriptGetDecoratorRendererForGoogleFirestore, TypeScriptGetDecoratorRendererForGoogleSpanner);
|
|
15
15
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { WorkspaceContext } from '@causa/workspace';
|
|
2
|
+
import { TypeScriptGetDecoratorRenderer, TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
3
|
+
/**
|
|
4
|
+
* Implements {@link TypeScriptGetDecoratorRenderer} for the {@link GoogleFirestoreRenderer}.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TypeScriptGetDecoratorRendererForGoogleFirestore extends TypeScriptGetDecoratorRenderer {
|
|
7
|
+
_call(): new (...args: any[]) => TypeScriptWithDecoratorsRenderer;
|
|
8
|
+
_supports(context: WorkspaceContext): boolean;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { WorkspaceContext } from '@causa/workspace';
|
|
2
|
+
import { TypeScriptGetDecoratorRenderer, TypeScriptWithDecoratorsRenderer, } from '@causa/workspace-typescript';
|
|
3
|
+
import { GoogleFirestoreRenderer } from '../../code-generation/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Implements {@link TypeScriptGetDecoratorRenderer} for the {@link GoogleFirestoreRenderer}.
|
|
6
|
+
*/
|
|
7
|
+
export class TypeScriptGetDecoratorRendererForGoogleFirestore extends TypeScriptGetDecoratorRenderer {
|
|
8
|
+
_call() {
|
|
9
|
+
return GoogleFirestoreRenderer;
|
|
10
|
+
}
|
|
11
|
+
_supports(context) {
|
|
12
|
+
return (context.get('project.language') === 'typescript' &&
|
|
13
|
+
this.generator === 'typescriptModelClass');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { WorkspaceContext } from '@causa/workspace';
|
|
2
|
-
import {
|
|
2
|
+
import { TypeScriptGetDecoratorRenderer, TypeScriptWithDecoratorsRenderer } from '@causa/workspace-typescript';
|
|
3
3
|
/**
|
|
4
4
|
* Implements {@link TypeScriptGetDecoratorRenderer} for the {@link GoogleSpannerRenderer}.
|
|
5
|
-
* The configuration name for the renderer is `google.spanner`.
|
|
6
5
|
*/
|
|
7
6
|
export declare class TypeScriptGetDecoratorRendererForGoogleSpanner extends TypeScriptGetDecoratorRenderer {
|
|
8
|
-
_call(): new (...args: any[]) =>
|
|
7
|
+
_call(): new (...args: any[]) => TypeScriptWithDecoratorsRenderer;
|
|
9
8
|
_supports(context: WorkspaceContext): boolean;
|
|
10
9
|
}
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import { WorkspaceContext } from '@causa/workspace';
|
|
2
|
-
import {
|
|
2
|
+
import { TypeScriptGetDecoratorRenderer, TypeScriptWithDecoratorsRenderer, } from '@causa/workspace-typescript';
|
|
3
3
|
import { GoogleSpannerRenderer } from '../../code-generation/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* Implements {@link TypeScriptGetDecoratorRenderer} for the {@link GoogleSpannerRenderer}.
|
|
6
|
-
* The configuration name for the renderer is `google.spanner`.
|
|
7
6
|
*/
|
|
8
7
|
export class TypeScriptGetDecoratorRendererForGoogleSpanner extends TypeScriptGetDecoratorRenderer {
|
|
9
8
|
_call() {
|
|
10
9
|
return GoogleSpannerRenderer;
|
|
11
10
|
}
|
|
12
11
|
_supports(context) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
const decoratorRenderers = context
|
|
17
|
-
.asConfiguration()
|
|
18
|
-
.get('typescript.codeGeneration.decoratorRenderers') ?? [];
|
|
19
|
-
return (decoratorRenderers.length === 0 ||
|
|
20
|
-
decoratorRenderers.includes('google.spanner'));
|
|
12
|
+
return (context.get('project.language') === 'typescript' &&
|
|
13
|
+
this.generator === 'typescriptModelClass');
|
|
21
14
|
}
|
|
22
15
|
}
|
|
@@ -2,6 +2,7 @@ import { WorkspaceContext } from '@causa/workspace';
|
|
|
2
2
|
import { EventTopicTriggerCreationError } from '@causa/workspace-core';
|
|
3
3
|
import { Subscription } from '@google-cloud/pubsub';
|
|
4
4
|
import { randomBytes } from 'crypto';
|
|
5
|
+
import { grpc } from 'google-gax';
|
|
5
6
|
import { CloudRunService } from './cloud-run.js';
|
|
6
7
|
import { IamService } from './iam.js';
|
|
7
8
|
import { PubSubService } from './pubsub.js';
|
|
@@ -109,7 +110,6 @@ export class CloudRunPubSubTriggerService {
|
|
|
109
110
|
}
|
|
110
111
|
this.invokerBindingIds.add(invokerBindingId);
|
|
111
112
|
this.logger.info(`🛂 Granting invoker IAM role to backfilling service account '${pubSubServiceAccount}' for Cloud Run service '${serviceId}'.`);
|
|
112
|
-
// This may fail due to eventual consistency during service account creation.
|
|
113
113
|
await this.cloudRunService.addInvokerBinding(serviceId, pubSubServiceAccount);
|
|
114
114
|
return invokerBindingId;
|
|
115
115
|
}
|
|
@@ -138,6 +138,8 @@ export class CloudRunPubSubTriggerService {
|
|
|
138
138
|
minimumBackoff: { seconds: 1, nanos: 0 },
|
|
139
139
|
maximumBackoff: { seconds: 60, nanos: 0 },
|
|
140
140
|
},
|
|
141
|
+
// This can occur due to eventual consistency when the service account is created.
|
|
142
|
+
gaxOpts: { retry: { retryCodes: [grpc.status.INVALID_ARGUMENT] } },
|
|
141
143
|
});
|
|
142
144
|
return subscriptionId;
|
|
143
145
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ServicesClient } from '@google-cloud/run';
|
|
2
|
+
import { grpc } from 'google-gax';
|
|
2
3
|
/**
|
|
3
4
|
* The role used to allow a service account to call a Cloud Run service.
|
|
4
5
|
*/
|
|
@@ -39,11 +40,9 @@ export class CloudRunService {
|
|
|
39
40
|
const members = [`serviceAccount:${serviceAccountEmail}`];
|
|
40
41
|
const binding = { role: INVOKER_ROLE, members };
|
|
41
42
|
policy.bindings = [...(policy.bindings ?? []), binding];
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
policy,
|
|
46
|
-
});
|
|
43
|
+
await this.servicesClient.setIamPolicy({ resource: serviceId, policy },
|
|
44
|
+
// This can occur due to eventual consistency when the service account is created.
|
|
45
|
+
{ retry: { retryCodes: [grpc.status.INVALID_ARGUMENT] } });
|
|
47
46
|
}
|
|
48
47
|
/**
|
|
49
48
|
* Removes a service account from the list of allowed invokers of a Cloud Run service.
|
|
@@ -17,16 +17,12 @@ export class ResourceManagerService {
|
|
|
17
17
|
* @returns The number of the GCP project.
|
|
18
18
|
*/
|
|
19
19
|
async getProjectNumber(projectId) {
|
|
20
|
-
const [projects] = await this.projectsClient.searchProjects({
|
|
21
|
-
query: `projectId:${projectId}`,
|
|
22
|
-
pageSize: 1,
|
|
23
|
-
});
|
|
20
|
+
const [projects] = await this.projectsClient.searchProjects({ query: `projectId:${projectId}`, pageSize: 1 }, { autoPaginate: false });
|
|
24
21
|
if (projects.length < 1) {
|
|
25
22
|
throw new Error(`Could not find GCP project '${projectId}'.`);
|
|
26
23
|
}
|
|
27
24
|
const [project] = projects;
|
|
28
|
-
|
|
29
|
-
const [projectsConst, projectNumber] = project.name?.split('/') ?? [];
|
|
25
|
+
const projectNumber = project.name?.split('/').at(1);
|
|
30
26
|
if (!projectNumber) {
|
|
31
27
|
throw new Error(`Failed to parse invalid project name '${project.name}'.`);
|
|
32
28
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@causa/workspace-google",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "The Causa workspace module providing many functionalities related to GCP and its services.",
|
|
5
|
-
"repository":
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/causa-io/workspace-module-google.git"
|
|
8
|
+
},
|
|
6
9
|
"license": "ISC",
|
|
7
10
|
"type": "module",
|
|
8
11
|
"engines": {
|
|
9
|
-
"node": ">=
|
|
12
|
+
"node": ">=20"
|
|
10
13
|
},
|
|
11
14
|
"main": "dist/index.js",
|
|
12
15
|
"types": "dist/index.d.ts",
|
|
@@ -29,47 +32,49 @@
|
|
|
29
32
|
"test:cov": "npm run test -- --coverage"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
|
-
"@causa/cli": ">= 0.6.
|
|
33
|
-
"@causa/workspace": ">= 0.
|
|
34
|
-
"@causa/workspace-core": ">= 0.
|
|
35
|
-
"@causa/workspace-typescript": ">= 0.
|
|
36
|
-
"@google-cloud/apikeys": "^2.
|
|
37
|
-
"@google-cloud/bigquery": "^8.
|
|
38
|
-
"@google-cloud/iam-credentials": "^4.0
|
|
39
|
-
"@google-cloud/pubsub": "^5.
|
|
40
|
-
"@google-cloud/resource-manager": "^6.0
|
|
41
|
-
"@google-cloud/run": "^2.0
|
|
42
|
-
"@google-cloud/secret-manager": "^6.0
|
|
43
|
-
"@google-cloud/service-usage": "^4.
|
|
44
|
-
"@google-cloud/spanner": "
|
|
35
|
+
"@causa/cli": ">= 0.6.2 < 1.0.0",
|
|
36
|
+
"@causa/workspace": ">= 0.18.0 < 1.0.0",
|
|
37
|
+
"@causa/workspace-core": ">= 0.23.0 < 1.0.0",
|
|
38
|
+
"@causa/workspace-typescript": ">= 0.11.0 < 1.0.0",
|
|
39
|
+
"@google-cloud/apikeys": "^2.2.0",
|
|
40
|
+
"@google-cloud/bigquery": "^8.1.1",
|
|
41
|
+
"@google-cloud/iam-credentials": "^4.2.0",
|
|
42
|
+
"@google-cloud/pubsub": "^5.1.0",
|
|
43
|
+
"@google-cloud/resource-manager": "^6.2.0",
|
|
44
|
+
"@google-cloud/run": "^2.3.0",
|
|
45
|
+
"@google-cloud/secret-manager": "^6.1.0",
|
|
46
|
+
"@google-cloud/service-usage": "^4.2.0",
|
|
47
|
+
"@google-cloud/spanner": "8.1.0",
|
|
45
48
|
"@google-cloud/storage": "^7.16.0",
|
|
46
49
|
"class-validator": "^0.14.2",
|
|
47
|
-
"firebase": "^
|
|
48
|
-
"firebase-admin": "^13.
|
|
50
|
+
"firebase": "^12.0.0",
|
|
51
|
+
"firebase-admin": "^13.4.0",
|
|
49
52
|
"globby": "^14.1.0",
|
|
50
|
-
"google-auth-library": "^
|
|
51
|
-
"google-gax": "
|
|
52
|
-
"googleapis": "^
|
|
53
|
-
"
|
|
54
|
-
"
|
|
53
|
+
"google-auth-library": "^10.2.1",
|
|
54
|
+
"google-gax": "5.0.1-rc.1",
|
|
55
|
+
"googleapis": "^155.0.0",
|
|
56
|
+
"micromatch": "^4.0.8",
|
|
57
|
+
"pino": "^9.7.0",
|
|
58
|
+
"quicktype-core": "^23.2.6",
|
|
55
59
|
"uuid": "^11.1.0"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
|
-
"@swc/core": "^1.
|
|
59
|
-
"@swc/jest": "^0.2.
|
|
60
|
-
"@tsconfig/node20": "^20.1.
|
|
61
|
-
"@types/jest": "^
|
|
62
|
-
"@types/
|
|
62
|
+
"@swc/core": "^1.13.3",
|
|
63
|
+
"@swc/jest": "^0.2.39",
|
|
64
|
+
"@tsconfig/node20": "^20.1.6",
|
|
65
|
+
"@types/jest": "^30.0.0",
|
|
66
|
+
"@types/micromatch": "^4.0.9",
|
|
67
|
+
"@types/node": "^22.17.0",
|
|
63
68
|
"@types/uuid": "^10.0.0",
|
|
64
69
|
"copyfiles": "^2.4.1",
|
|
65
|
-
"eslint": "^9.
|
|
66
|
-
"eslint-config-prettier": "^10.1.
|
|
67
|
-
"eslint-plugin-prettier": "^5.
|
|
68
|
-
"jest": "^
|
|
69
|
-
"jest-extended": "^
|
|
70
|
+
"eslint": "^9.32.0",
|
|
71
|
+
"eslint-config-prettier": "^10.1.8",
|
|
72
|
+
"eslint-plugin-prettier": "^5.5.3",
|
|
73
|
+
"jest": "^30.0.5",
|
|
74
|
+
"jest-extended": "^6.0.0",
|
|
70
75
|
"rimraf": "^6.0.1",
|
|
71
76
|
"ts-node": "^10.9.2",
|
|
72
|
-
"typescript": "^5.
|
|
73
|
-
"typescript-eslint": "^8.
|
|
77
|
+
"typescript": "^5.9.2",
|
|
78
|
+
"typescript-eslint": "^8.39.0"
|
|
74
79
|
}
|
|
75
80
|
}
|