@causa/runtime-google 0.39.1 → 1.0.0-rc.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 (73) hide show
  1. package/README.md +7 -14
  2. package/dist/app-check/testing.d.ts +7 -3
  3. package/dist/app-check/testing.js +9 -6
  4. package/dist/firebase/module.d.ts +14 -2
  5. package/dist/firebase/module.js +45 -16
  6. package/dist/firebase/testing.d.ts +7 -4
  7. package/dist/firebase/testing.js +12 -7
  8. package/dist/firestore/testing.d.ts +32 -17
  9. package/dist/firestore/testing.js +45 -29
  10. package/dist/identity-platform/testing.d.ts +21 -6
  11. package/dist/identity-platform/testing.js +34 -9
  12. package/dist/pubsub/publisher.module.d.ts +1 -1
  13. package/dist/pubsub/{testing/fixture.d.ts → testing.d.ts} +64 -48
  14. package/dist/pubsub/{testing/fixture.js → testing.js} +106 -73
  15. package/dist/spanner/column.decorator.d.ts +1 -11
  16. package/dist/spanner/column.decorator.js +2 -7
  17. package/dist/spanner/conversion.d.ts +5 -18
  18. package/dist/spanner/conversion.js +45 -125
  19. package/dist/spanner/entity-manager.d.ts +19 -23
  20. package/dist/spanner/entity-manager.js +26 -60
  21. package/dist/spanner/table-cache.js +0 -3
  22. package/dist/spanner/testing.d.ts +37 -18
  23. package/dist/spanner/testing.js +75 -10
  24. package/dist/spanner/types.d.ts +0 -6
  25. package/dist/testing.d.ts +36 -2
  26. package/dist/testing.js +33 -2
  27. package/dist/transaction/firestore-pubsub/index.d.ts +3 -2
  28. package/dist/transaction/firestore-pubsub/index.js +2 -1
  29. package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.d.ts +1 -1
  30. package/dist/transaction/firestore-pubsub/nestjs-collection-resolver.js +0 -1
  31. package/dist/transaction/firestore-pubsub/readonly-state-transaction.d.ts +37 -0
  32. package/dist/transaction/firestore-pubsub/readonly-state-transaction.js +46 -0
  33. package/dist/transaction/firestore-pubsub/runner.d.ts +6 -4
  34. package/dist/transaction/firestore-pubsub/runner.js +16 -7
  35. package/dist/transaction/firestore-pubsub/state-transaction.d.ts +11 -49
  36. package/dist/transaction/firestore-pubsub/state-transaction.js +19 -42
  37. package/dist/transaction/firestore-pubsub/transaction.d.ts +15 -3
  38. package/dist/transaction/firestore-pubsub/transaction.js +21 -3
  39. package/dist/transaction/firestore-pubsub/types.d.ts +36 -0
  40. package/dist/transaction/firestore-pubsub/types.js +1 -0
  41. package/dist/transaction/index.d.ts +0 -3
  42. package/dist/transaction/index.js +0 -3
  43. package/dist/transaction/spanner-outbox/index.d.ts +3 -1
  44. package/dist/transaction/spanner-outbox/index.js +3 -0
  45. package/dist/transaction/spanner-outbox/readonly-transaction.d.ts +22 -0
  46. package/dist/transaction/spanner-outbox/readonly-transaction.js +25 -0
  47. package/dist/transaction/spanner-outbox/runner.d.ts +7 -18
  48. package/dist/transaction/spanner-outbox/runner.js +13 -6
  49. package/dist/transaction/{spanner-utils.js → spanner-outbox/spanner-utils.js} +1 -1
  50. package/dist/transaction/spanner-outbox/state-transaction.d.ts +20 -0
  51. package/dist/transaction/spanner-outbox/state-transaction.js +34 -0
  52. package/dist/transaction/spanner-outbox/transaction.d.ts +28 -0
  53. package/dist/transaction/spanner-outbox/transaction.js +38 -0
  54. package/package.json +37 -34
  55. package/dist/pubsub/testing/index.d.ts +0 -4
  56. package/dist/pubsub/testing/index.js +0 -2
  57. package/dist/pubsub/testing/requester.d.ts +0 -50
  58. package/dist/pubsub/testing/requester.js +0 -53
  59. package/dist/testing/google-app-fixture.d.ts +0 -191
  60. package/dist/testing/google-app-fixture.js +0 -200
  61. package/dist/testing/index.d.ts +0 -1
  62. package/dist/testing/index.js +0 -1
  63. package/dist/transaction/spanner-pubsub/index.d.ts +0 -2
  64. package/dist/transaction/spanner-pubsub/index.js +0 -2
  65. package/dist/transaction/spanner-pubsub/module.d.ts +0 -14
  66. package/dist/transaction/spanner-pubsub/module.js +0 -21
  67. package/dist/transaction/spanner-pubsub/runner.d.ts +0 -23
  68. package/dist/transaction/spanner-pubsub/runner.js +0 -69
  69. package/dist/transaction/spanner-state-transaction.d.ts +0 -20
  70. package/dist/transaction/spanner-state-transaction.js +0 -35
  71. package/dist/transaction/spanner-transaction.d.ts +0 -16
  72. package/dist/transaction/spanner-transaction.js +0 -20
  73. /package/dist/transaction/{spanner-utils.d.ts → spanner-outbox/spanner-utils.d.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { type ObjectSerializer } from '@causa/runtime';
2
- import type { NestJsModuleOverrider } from '@causa/runtime/nestjs/testing';
2
+ import type { AppFixture, EventFixture, Fixture, NestJsModuleOverrider } from '@causa/runtime/nestjs/testing';
3
3
  import { PubSub, Subscription, Topic } from '@google-cloud/pubsub';
4
- import type { Type } from '@nestjs/common';
4
+ import { type Type } from '@nestjs/common';
5
5
  /**
6
6
  * A received Pub/Sub message that was deserialized.
7
7
  */
@@ -20,27 +20,51 @@ export type ReceivedPubSubEvent = {
20
20
  event: any;
21
21
  };
22
22
  /**
23
- * Options for the {@link PubSubFixture.expectMessageInTopic} method.
23
+ * Options for the {@link PubSubFixture.expectMessage} method.
24
24
  */
25
- type ExpectMessageInTopicOptions = {
25
+ export type ExpectMessageOptions = {
26
26
  /**
27
27
  * The maximum time (in milliseconds) to wait for a message before giving up.
28
28
  * Defaults to `2000`.
29
29
  */
30
30
  timeout?: number;
31
31
  };
32
+ /**
33
+ * Options when making a request to an endpoint handling Pub/Sub events using an {@link EventRequester}.
34
+ */
35
+ export type EventRequesterOptions = {
36
+ /**
37
+ * The attributes to add to the Pub/Sub message.
38
+ * Using `undefined` values allows removing the attributes set by default.
39
+ */
40
+ attributes?: Record<string, string | undefined>;
41
+ /**
42
+ * The expected status code when making the request.
43
+ * Default is `200`.
44
+ */
45
+ expectedStatus?: number;
46
+ /**
47
+ * The time to set as the publication time of the Pub/Sub message.
48
+ */
49
+ publishTime?: Date;
50
+ };
51
+ /**
52
+ * A function that makes a query to an endpoint handling Pub/Sub events and tests the response.
53
+ */
54
+ export type EventRequester = (event: object, options?: EventRequesterOptions) => Promise<void>;
32
55
  /**
33
56
  * A utility class managing temporary Pub/Sub topics and listening to messages published to them.
34
57
  */
35
- export declare class PubSubFixture {
58
+ export declare class PubSubFixture implements Fixture, EventFixture {
59
+ private readonly topicsAndTypes;
36
60
  /**
37
- * The Pub/Sub client to use.
61
+ * The parent {@link AppFixture}.
38
62
  */
39
- readonly pubSub: PubSub;
63
+ private appFixture;
40
64
  /**
41
- * Whether the Pub/Sub client is managed by the fixture.
65
+ * The Pub/Sub client to use.
42
66
  */
43
- private readonly isManagedClient;
67
+ readonly pubSub: PubSub;
44
68
  /**
45
69
  * The (de)serializer to use for Pub/Sub messages.
46
70
  */
@@ -49,7 +73,7 @@ export declare class PubSubFixture {
49
73
  * The dictionary of monitored temporary topics.
50
74
  * The key is the name of the event topic (not broker-specific).
51
75
  */
52
- readonly fixtures: Record<string, {
76
+ readonly topics: Record<string, {
53
77
  /**
54
78
  * The created temporary Pub/Sub topic.
55
79
  */
@@ -66,13 +90,10 @@ export declare class PubSubFixture {
66
90
  /**
67
91
  * Creates a new {@link PubSubFixture}.
68
92
  *
93
+ * @param topicsAndTypes The dictionary of topics to test and their event types.
69
94
  * @param options Options for the fixture.
70
95
  */
71
- constructor(options?: {
72
- /**
73
- * The Pub/Sub client to use.
74
- */
75
- pubSub?: PubSub;
96
+ constructor(topicsAndTypes: Record<string, Type>, options?: {
76
97
  /**
77
98
  * The (de)serializer to use for Pub/Sub messages.
78
99
  */
@@ -81,44 +102,46 @@ export declare class PubSubFixture {
81
102
  /**
82
103
  * Creates a new temporary topic and starts listening to messages published to it.
83
104
  *
84
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
105
+ * @param sourceTopic The original name of the topic, i.e. the one used in production.
85
106
  * @param eventType The type of the event published to the topic, used for deserialization.
86
107
  * @returns The configuration key and value for the created temporary topic.
87
108
  */
88
- create(sourceTopicName: string, eventType: Type): Promise<Record<string, string>>;
89
- /**
90
- * Creates several temporary topics and returns the configuration (environment variables) for the
91
- *
92
- * @param topicsAndTypes A dictionary of topics and their corresponding event types.
93
- * @returns The configuration for Pub/Sub topics, with the IDs of the created topics.
94
- */
95
- createMany(topicsAndTypes: Record<string, Type>): Promise<Record<string, string>>;
109
+ private create;
110
+ init(appFixture: AppFixture): Promise<NestJsModuleOverrider>;
96
111
  /**
97
- * Uses {@link PubSubFixture.createMany} to create temporary topics and returns a {@link NestJsModuleOverrider} to
98
- * override the Pub/Sub publisher configuration.
112
+ * Creates an {@link EventRequester} for an endpoint handling Pub/Sub messages.
113
+ * If the `event` passed to the {@link EventRequester} conforms to the `Event` interface (if it has `producedAt`,
114
+ * `name` and / or `id` properties), the default attributes are set in the Pub/Sub message. Default attributes can be
115
+ * overridden (or removed by passing `undefined`) using {@link EventRequesterOptions.attributes}.
99
116
  *
100
- * @param topicsAndTypes A dictionary of topics and their corresponding event types.
101
- * @returns The {@link NestJsModuleOverrider} to use to override the Pub/Sub publisher configuration.
117
+ * @param endpoint The endpoint to query.
118
+ * @param options Options when creating the requester.
119
+ * @returns The {@link EventRequester}.
102
120
  */
103
- createWithOverrider(topicsAndTypes: Record<string, Type>): Promise<NestJsModuleOverrider>;
121
+ makeRequester(endpoint: string, options?: {
122
+ /**
123
+ * The default expected status code when making a request.
124
+ */
125
+ expectedStatus?: number;
126
+ }): EventRequester;
104
127
  /**
105
128
  * Checks that the given message has been published to the specified topic.
106
129
  *
107
- * @param sourceTopicName The original name of the event topic.
130
+ * @param topic The original name of the event topic.
108
131
  * @param expectedMessage The message expected to have been published.
109
132
  * This can be an `expect` expression, e.g. `expect.objectContaining({})`.
110
133
  * @param options Options for the expectation.
111
134
  */
112
- expectMessageInTopic(sourceTopicName: string, expectedMessage: any, options?: ExpectMessageInTopicOptions): Promise<void>;
135
+ expectMessage(topic: string, expectedMessage: any, options?: ExpectMessageOptions): Promise<void>;
113
136
  /**
114
- * Uses {@link PubSubFixture.expectMessageInTopic} to check that the given event has been published to the specified
137
+ * Uses {@link PubSubFixture.expectMessage} to check that the given event has been published to the specified
115
138
  * topic. The `expectedEvent` is the payload of the message, i.e. the `event` property.
116
139
  *
117
- * @param sourceTopicName The original name of the event topic.
140
+ * @param topic The original name of the event topic.
118
141
  * @param expectedEvent The event expected to have been published.
119
142
  * @param options Options for the expectation.
120
143
  */
121
- expectEventInTopic(sourceTopicName: string, expectedEvent: any, options?: ExpectMessageInTopicOptions & {
144
+ expectEvent(topic: string, expectedEvent: any, options?: ExpectMessageOptions & {
122
145
  /**
123
146
  * The attributes expected to have been published with the event.
124
147
  * This may contain only a subset of the attributes.
@@ -130,29 +153,22 @@ export declare class PubSubFixture {
130
153
  * By default, because publishing (and receiving) the messages is asynchronous, a small delay is added before checking
131
154
  * that no message has been received. This delay can be removed or increased by passing the `delay` option.
132
155
  *
133
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
156
+ * @param topic The original name of the topic, i.e. the one used in production.
134
157
  * @param options Options for the expectation.
135
158
  */
136
- expectNoMessageInTopic(sourceTopicName: string, options?: {
159
+ expectNoMessage(topic: string, options?: {
137
160
  /**
138
161
  * The delay (in milliseconds) before checking that no message has been received.
139
162
  */
140
163
  delay?: number;
141
164
  }): Promise<void>;
142
- /**
143
- * Clears all the messages received from the temporary topics.
144
- */
145
- clear(): void;
165
+ expectNoEvent(topic: string): Promise<void>;
166
+ clear(): Promise<void>;
146
167
  /**
147
168
  * Deletes the temporary topic for the corresponding "production" topic.
148
169
  *
149
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
150
- */
151
- delete(sourceTopicName: string): Promise<void>;
152
- /**
153
- * Deletes all previously created temporary topics.
154
- * If the Pub/Sub client was managed by the fixture (it wasn't passed as an option), it is also closed.
170
+ * @param topic The original name of the topic, i.e. the one used in production.
155
171
  */
156
- deleteAll(): Promise<void>;
172
+ deleteTopic(topic: string): Promise<void>;
173
+ delete(): Promise<void>;
157
174
  }
158
- export {};
@@ -1,9 +1,10 @@
1
1
  import { JsonObjectSerializer } from '@causa/runtime';
2
2
  import { Message, PubSub, Subscription, Topic } from '@google-cloud/pubsub';
3
+ import { HttpStatus } from '@nestjs/common';
3
4
  import { setTimeout } from 'timers/promises';
4
5
  import * as uuid from 'uuid';
5
- import { getConfigurationKeyForTopic } from '../configuration.js';
6
- import { PUBSUB_PUBLISHER_CONFIGURATION_GETTER_INJECTION_NAME } from '../publisher.module.js';
6
+ import { getConfigurationKeyForTopic } from './configuration.js';
7
+ import { PUBSUB_PUBLISHER_CONFIGURATION_GETTER_INJECTION_NAME } from './publisher.module.js';
7
8
  /**
8
9
  * The default duration (in milliseconds) after which `expectMessageInTopic` times out.
9
10
  */
@@ -20,14 +21,15 @@ const DURATION_BETWEEN_EXPECT_ATTEMPTS = 50;
20
21
  * A utility class managing temporary Pub/Sub topics and listening to messages published to them.
21
22
  */
22
23
  export class PubSubFixture {
24
+ topicsAndTypes;
23
25
  /**
24
- * The Pub/Sub client to use.
26
+ * The parent {@link AppFixture}.
25
27
  */
26
- pubSub;
28
+ appFixture;
27
29
  /**
28
- * Whether the Pub/Sub client is managed by the fixture.
30
+ * The Pub/Sub client to use.
29
31
  */
30
- isManagedClient;
32
+ pubSub;
31
33
  /**
32
34
  * The (de)serializer to use for Pub/Sub messages.
33
35
  */
@@ -36,85 +38,122 @@ export class PubSubFixture {
36
38
  * The dictionary of monitored temporary topics.
37
39
  * The key is the name of the event topic (not broker-specific).
38
40
  */
39
- fixtures = {};
41
+ topics = {};
40
42
  /**
41
43
  * Creates a new {@link PubSubFixture}.
42
44
  *
45
+ * @param topicsAndTypes The dictionary of topics to test and their event types.
43
46
  * @param options Options for the fixture.
44
47
  */
45
- constructor(options = {}) {
46
- this.pubSub = options.pubSub ?? new PubSub();
47
- this.isManagedClient = !options.pubSub;
48
+ constructor(topicsAndTypes, options = {}) {
49
+ this.topicsAndTypes = topicsAndTypes;
50
+ this.pubSub = new PubSub();
48
51
  this.serializer = options.serializer ?? new JsonObjectSerializer();
49
52
  }
50
53
  /**
51
54
  * Creates a new temporary topic and starts listening to messages published to it.
52
55
  *
53
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
56
+ * @param sourceTopic The original name of the topic, i.e. the one used in production.
54
57
  * @param eventType The type of the event published to the topic, used for deserialization.
55
58
  * @returns The configuration key and value for the created temporary topic.
56
59
  */
57
- async create(sourceTopicName, eventType) {
58
- await this.delete(sourceTopicName);
60
+ async create(sourceTopic, eventType) {
61
+ await this.deleteTopic(sourceTopic);
59
62
  const suffix = uuid.v4().slice(-10);
60
- const topicName = `${sourceTopicName}-${suffix}`;
63
+ const topicName = `${sourceTopic}-${suffix}`;
61
64
  const subscriptionName = `fixture-${suffix}`;
62
65
  // This ensures the project ID is populated in the Pub/Sub client.
63
66
  // Because the configuration is cached, it should be okay to call this multiple times.
64
67
  await this.pubSub.getClientConfig();
65
68
  const [topic] = await this.pubSub.createTopic(topicName);
66
69
  const [subscription] = await topic.createSubscription(subscriptionName);
67
- this.fixtures[sourceTopicName] = { topic, subscription, messages: [] };
70
+ this.topics[sourceTopic] = { topic, subscription, messages: [] };
68
71
  subscription.on('message', async (message) => {
69
- const fixture = this.fixtures[sourceTopicName];
70
- if (!fixture || fixture.topic !== topic) {
72
+ const topicFixture = this.topics[sourceTopic];
73
+ if (topicFixture?.topic !== topic) {
71
74
  return;
72
75
  }
73
76
  const event = await this.serializer.deserialize(eventType, message.data);
74
- fixture.messages.push({
77
+ topicFixture.messages.push({
75
78
  attributes: message.attributes,
76
79
  orderingKey: message.orderingKey ? message.orderingKey : undefined,
77
80
  event,
78
81
  });
79
82
  message.ack();
80
83
  });
81
- return { [getConfigurationKeyForTopic(sourceTopicName)]: topic.name };
84
+ return { [getConfigurationKeyForTopic(sourceTopic)]: topic.name };
82
85
  }
83
- /**
84
- * Creates several temporary topics and returns the configuration (environment variables) for the
85
- *
86
- * @param topicsAndTypes A dictionary of topics and their corresponding event types.
87
- * @returns The configuration for Pub/Sub topics, with the IDs of the created topics.
88
- */
89
- async createMany(topicsAndTypes) {
90
- const configurations = await Promise.all(Object.entries(topicsAndTypes).map(async ([topicName, eventType]) => this.create(topicName, eventType)));
91
- return Object.assign({}, ...configurations);
86
+ async init(appFixture) {
87
+ this.appFixture = appFixture;
88
+ const configurations = await Promise.all(Object.entries(this.topicsAndTypes).map(([topic, eventType]) => this.create(topic, eventType)));
89
+ const configuration = Object.assign({}, ...configurations);
90
+ return (builder) => builder
91
+ .overrideProvider(PUBSUB_PUBLISHER_CONFIGURATION_GETTER_INJECTION_NAME)
92
+ .useValue((key) => configuration[key]);
92
93
  }
93
94
  /**
94
- * Uses {@link PubSubFixture.createMany} to create temporary topics and returns a {@link NestJsModuleOverrider} to
95
- * override the Pub/Sub publisher configuration.
95
+ * Creates an {@link EventRequester} for an endpoint handling Pub/Sub messages.
96
+ * If the `event` passed to the {@link EventRequester} conforms to the `Event` interface (if it has `producedAt`,
97
+ * `name` and / or `id` properties), the default attributes are set in the Pub/Sub message. Default attributes can be
98
+ * overridden (or removed by passing `undefined`) using {@link EventRequesterOptions.attributes}.
96
99
  *
97
- * @param topicsAndTypes A dictionary of topics and their corresponding event types.
98
- * @returns The {@link NestJsModuleOverrider} to use to override the Pub/Sub publisher configuration.
100
+ * @param endpoint The endpoint to query.
101
+ * @param options Options when creating the requester.
102
+ * @returns The {@link EventRequester}.
99
103
  */
100
- async createWithOverrider(topicsAndTypes) {
101
- const configuration = await this.createMany(topicsAndTypes);
102
- return (builder) => builder
103
- .overrideProvider(PUBSUB_PUBLISHER_CONFIGURATION_GETTER_INJECTION_NAME)
104
- .useValue((key) => configuration[key]);
104
+ makeRequester(endpoint, options = {}) {
105
+ return async (event, requestOptions) => {
106
+ const messageId = uuid.v4();
107
+ const publishTime = (requestOptions?.publishTime ?? new Date()).toISOString();
108
+ const buffer = await this.serializer.serialize(event);
109
+ const data = buffer.toString('base64');
110
+ // Default attributes if the event conforms to the `Event` interface.
111
+ const defaultAttributes = {};
112
+ if ('producedAt' in event && event.producedAt instanceof Date) {
113
+ defaultAttributes.producedAt = event.producedAt.toISOString();
114
+ }
115
+ if ('name' in event && typeof event.name === 'string') {
116
+ defaultAttributes.eventName = event.name;
117
+ }
118
+ if ('id' in event && typeof event.id === 'string') {
119
+ defaultAttributes.eventId = event.id;
120
+ }
121
+ const attributes = {
122
+ ...defaultAttributes,
123
+ ...requestOptions?.attributes,
124
+ };
125
+ const payload = {
126
+ message: {
127
+ messageId,
128
+ message_id: messageId,
129
+ publishTime,
130
+ publish_time: publishTime,
131
+ attributes,
132
+ data,
133
+ },
134
+ subscription: 'subscription',
135
+ };
136
+ const expectedStatus = requestOptions?.expectedStatus ??
137
+ options.expectedStatus ??
138
+ HttpStatus.OK;
139
+ await this.appFixture.request
140
+ .post(endpoint)
141
+ .send(payload)
142
+ .expect(expectedStatus);
143
+ };
105
144
  }
106
145
  /**
107
146
  * Checks that the given message has been published to the specified topic.
108
147
  *
109
- * @param sourceTopicName The original name of the event topic.
148
+ * @param topic The original name of the event topic.
110
149
  * @param expectedMessage The message expected to have been published.
111
150
  * This can be an `expect` expression, e.g. `expect.objectContaining({})`.
112
151
  * @param options Options for the expectation.
113
152
  */
114
- async expectMessageInTopic(sourceTopicName, expectedMessage, options = {}) {
115
- const fixture = this.fixtures[sourceTopicName];
153
+ async expectMessage(topic, expectedMessage, options = {}) {
154
+ const fixture = this.topics[topic];
116
155
  if (!fixture) {
117
- throw new Error(`Fixture for topic '${sourceTopicName}' does not exist.`);
156
+ throw new Error(`Fixture for topic '${topic}' does not exist.`);
118
157
  }
119
158
  const timeoutTime = new Date().getTime() + (options.timeout ?? DEFAULT_EXPECT_TIMEOUT);
120
159
  while (true) {
@@ -136,15 +175,15 @@ export class PubSubFixture {
136
175
  }
137
176
  }
138
177
  /**
139
- * Uses {@link PubSubFixture.expectMessageInTopic} to check that the given event has been published to the specified
178
+ * Uses {@link PubSubFixture.expectMessage} to check that the given event has been published to the specified
140
179
  * topic. The `expectedEvent` is the payload of the message, i.e. the `event` property.
141
180
  *
142
- * @param sourceTopicName The original name of the event topic.
181
+ * @param topic The original name of the event topic.
143
182
  * @param expectedEvent The event expected to have been published.
144
183
  * @param options Options for the expectation.
145
184
  */
146
- async expectEventInTopic(sourceTopicName, expectedEvent, options = {}) {
147
- await this.expectMessageInTopic(sourceTopicName, expect.objectContaining({
185
+ async expectEvent(topic, expectedEvent, options = {}) {
186
+ await this.expectMessage(topic, expect.objectContaining({
148
187
  event: expectedEvent,
149
188
  attributes: expect.objectContaining(options.attributes ?? {}),
150
189
  }), options);
@@ -154,13 +193,13 @@ export class PubSubFixture {
154
193
  * By default, because publishing (and receiving) the messages is asynchronous, a small delay is added before checking
155
194
  * that no message has been received. This delay can be removed or increased by passing the `delay` option.
156
195
  *
157
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
196
+ * @param topic The original name of the topic, i.e. the one used in production.
158
197
  * @param options Options for the expectation.
159
198
  */
160
- async expectNoMessageInTopic(sourceTopicName, options = {}) {
161
- const fixture = this.fixtures[sourceTopicName];
199
+ async expectNoMessage(topic, options = {}) {
200
+ const fixture = this.topics[topic];
162
201
  if (!fixture) {
163
- throw new Error(`Fixture for topic '${sourceTopicName}' does not exist.`);
202
+ throw new Error(`Fixture for topic '${topic}' does not exist.`);
164
203
  }
165
204
  const delay = options.delay ?? DEFAULT_EXPECT_NO_MESSAGE_DELAY;
166
205
  if (delay > 0) {
@@ -168,40 +207,34 @@ export class PubSubFixture {
168
207
  }
169
208
  const numMessages = fixture.messages.length;
170
209
  if (numMessages > 0) {
171
- throw new Error(`Expected 0 messages in '${sourceTopicName}' but found ${numMessages}.`);
210
+ throw new Error(`Expected 0 messages in '${topic}' but found ${numMessages}.`);
172
211
  }
173
212
  }
174
- /**
175
- * Clears all the messages received from the temporary topics.
176
- */
177
- clear() {
178
- Object.values(this.fixtures).forEach((f) => {
179
- f.messages = [];
213
+ async expectNoEvent(topic) {
214
+ await this.expectNoMessage(topic);
215
+ }
216
+ async clear() {
217
+ Object.values(this.topics).forEach((t) => {
218
+ t.messages = [];
180
219
  });
181
220
  }
182
221
  /**
183
222
  * Deletes the temporary topic for the corresponding "production" topic.
184
223
  *
185
- * @param sourceTopicName The original name of the topic, i.e. the one used in production.
224
+ * @param topic The original name of the topic, i.e. the one used in production.
186
225
  */
187
- async delete(sourceTopicName) {
188
- const fixture = this.fixtures[sourceTopicName];
189
- if (!fixture) {
226
+ async deleteTopic(topic) {
227
+ const topicFixture = this.topics[topic];
228
+ if (!topicFixture) {
190
229
  return;
191
230
  }
192
- delete this.fixtures[sourceTopicName];
193
- fixture.subscription.removeAllListeners();
194
- await fixture.subscription.delete();
195
- await fixture.topic.delete();
231
+ delete this.topics[topic];
232
+ topicFixture.subscription.removeAllListeners();
233
+ await topicFixture.subscription.delete();
234
+ await topicFixture.topic.delete();
196
235
  }
197
- /**
198
- * Deletes all previously created temporary topics.
199
- * If the Pub/Sub client was managed by the fixture (it wasn't passed as an option), it is also closed.
200
- */
201
- async deleteAll() {
202
- await Promise.all(Object.keys(this.fixtures).map((sourceTopicName) => this.delete(sourceTopicName)));
203
- if (this.isManagedClient) {
204
- await this.pubSub.close();
205
- }
236
+ async delete() {
237
+ await Promise.all(Object.keys(this.topics).map((t) => this.deleteTopic(t)));
238
+ await this.pubSub.close();
206
239
  }
207
240
  }
@@ -8,15 +8,6 @@ export type SpannerColumnMetadata = {
8
8
  * The name of the Spanner column for this property.
9
9
  */
10
10
  name: string;
11
- /**
12
- * If the type of the property is a nested type, this is the class for this property.
13
- */
14
- nestedType?: Type;
15
- /**
16
- * When `true`, sets the property to null if all properties is the nested object are null.
17
- * Defaults to `false`.
18
- */
19
- nullifyNested: boolean;
20
11
  /**
21
12
  * When `true`, the column is assumed to be of type `INT64` and the value will be safely stored in a `bigint`.
22
13
  */
@@ -60,7 +51,6 @@ export declare function getSpannerColumnsMetadata(classType: Type): SpannerColum
60
51
  * Lists all the columns in the given class, based on {@link SpannerColumn} decorators.
61
52
  *
62
53
  * @param classType The type for the table.
63
- * @param namePrefix Used for recursion with nested types, do not use directly.
64
54
  * @returns The list of columns for the given class.
65
55
  */
66
- export declare function getSpannerColumns(classType: Type, namePrefix?: string): string[];
56
+ export declare function getSpannerColumns(classType: Type): string[];
@@ -16,8 +16,6 @@ export function SpannerColumn(options = {}) {
16
16
  const isPreciseDate = options.isPreciseDate ?? false;
17
17
  metadata[propertyKey] = {
18
18
  name: options.name ?? propertyKey,
19
- nestedType: options.nestedType,
20
- nullifyNested: options.nullifyNested ?? false,
21
19
  isInt: options.isInt ?? false,
22
20
  isBigInt: options.isBigInt ?? false,
23
21
  isPreciseDate,
@@ -55,12 +53,9 @@ export function getSpannerColumnsMetadata(classType) {
55
53
  * Lists all the columns in the given class, based on {@link SpannerColumn} decorators.
56
54
  *
57
55
  * @param classType The type for the table.
58
- * @param namePrefix Used for recursion with nested types, do not use directly.
59
56
  * @returns The list of columns for the given class.
60
57
  */
61
- export function getSpannerColumns(classType, namePrefix = '') {
58
+ export function getSpannerColumns(classType) {
62
59
  const columnsMetadata = getSpannerColumnsMetadata(classType);
63
- return Object.values(columnsMetadata).flatMap((c) => c.nestedType
64
- ? getSpannerColumns(c.nestedType, `${c.name}_`)
65
- : `${namePrefix}${c.name}`);
60
+ return Object.values(columnsMetadata).flatMap((c) => c.name);
66
61
  }
@@ -1,5 +1,4 @@
1
1
  import type { Type } from '@nestjs/common';
2
- import type { RecursivePartialEntity } from './types.js';
3
2
  /**
4
3
  * Creates a typed class instance from an object returned by the Spanner API.
5
4
  *
@@ -13,35 +12,23 @@ export declare function spannerObjectToInstance<T>(spannerObject: Record<string,
13
12
  *
14
13
  * @param instance The object to convert to a Spanner object.
15
14
  * @param type The type of the object to convert.
16
- * @param columnNamePrefix The prefix to add to column names. Used for recursion and should not be set directly.
17
15
  * @returns A generic JavaScript object that can be passed to the Spanner API.
18
16
  */
19
- export declare function instanceToSpannerObjectInternal<T>(instance: T | RecursivePartialEntity<T> | null, type: Type<T>, columnNamePrefix?: string): Record<string, any>;
17
+ export declare function instanceToSpannerObject<T>(instance: T | Partial<T>, type: Type<T>): Record<string, any>;
20
18
  /**
21
- * Converts a class object to a plain JavaScript object that can be passed to the Spanner API.
22
- *
23
- * @param instance The object to convert to a Spanner object.
24
- * @param type The type of the object to convert.
25
- * @returns A generic JavaScript object that can be passed to the Spanner API.
26
- */
27
- export declare function instanceToSpannerObject<T>(instance: T | RecursivePartialEntity<T>, type: Type<T>): Record<string, any>;
28
- /**
29
- * Copies an instance, recursively setting all columns that are not defined in the instance to `null`.
30
- * Columns with the {@link SpannerColumnMetadata.nullifyNested} option set to `true` are also set to `null`.
19
+ * Copies an instance, setting all columns that are not defined in the instance to `null`.
31
20
  *
32
21
  * @param instance The instance to copy.
33
22
  * @param type The type of the instance.
34
23
  * @returns The copied instance.
35
24
  */
36
- export declare function copyInstanceWithMissingColumnsToNull<T>(instance: T | RecursivePartialEntity<T>, type: Type<T>): T;
25
+ export declare function copyInstanceWithMissingColumnsToNull<T>(instance: T | Partial<T>, type: Type<T>): T;
37
26
  /**
38
- * Recursively updates an instance with the values from the update.
39
- * Updates are applied "column-wise", which means that recursion stops at properties decorated as columns.
40
- * For example, JSON values are not affected.
27
+ * Updates an instance with the values from the update.
41
28
  *
42
29
  * @param instance The instance to update. It should be a full, typed, instance, unless `type` is passed as well.
43
30
  * @param update The update to apply to the instance.
44
31
  * @param type The type of the instance. If not provided, it will be inferred from the instance.
45
32
  * @returns The updated instance.
46
33
  */
47
- export declare function updateInstanceByColumn<T>(instance: T, update: RecursivePartialEntity<T>, type?: Type<T>): T;
34
+ export declare function updateInstanceByColumn<T>(instance: T, update: Partial<T>, type?: Type<T>): T;