@causa/runtime-google 0.35.0 → 0.35.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,8 @@
1
1
  import { type ObjectSerializer } from '@causa/runtime';
2
- import { BaseEventHandlerInterceptor, type ParsedEventRequest } from '@causa/runtime/nestjs';
2
+ import { BaseEventHandlerInterceptor, Logger, type ParsedEventRequest } from '@causa/runtime/nestjs';
3
3
  import { type ExecutionContext, type Type } from '@nestjs/common';
4
4
  import { Reflector } from '@nestjs/core';
5
5
  import type { Request } from 'express';
6
- import { PinoLogger } from 'nestjs-pino';
7
6
  /**
8
7
  * The ID of the Pub/Sub event handler interceptor, that can passed to the `UseEventHandler` decorator.
9
8
  */
@@ -35,7 +34,7 @@ declare class PubSubMessage {
35
34
  */
36
35
  export declare class PubSubEventHandlerInterceptor extends BaseEventHandlerInterceptor {
37
36
  protected readonly serializer: ObjectSerializer;
38
- constructor(serializer: ObjectSerializer, reflector: Reflector, logger: PinoLogger);
37
+ constructor(serializer: ObjectSerializer, reflector: Reflector, logger: Logger);
39
38
  /**
40
39
  * Parses the given request as the payload of a Pub/Sub push request.
41
40
  *
@@ -9,11 +9,10 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  var PubSubEventHandlerInterceptor_1;
11
11
  import { AllowMissing, IsDateType, ValidateNestedType, ValidationError, parseObject, validateObject, } from '@causa/runtime';
12
- import { BadRequestError, BaseEventHandlerInterceptor, } from '@causa/runtime/nestjs';
12
+ import { BadRequestError, BaseEventHandlerInterceptor, Logger, } from '@causa/runtime/nestjs';
13
13
  import { Injectable } from '@nestjs/common';
14
14
  import { Reflector } from '@nestjs/core';
15
15
  import { IsBase64, IsObject, IsString } from 'class-validator';
16
- import { PinoLogger } from 'nestjs-pino';
17
16
  /**
18
17
  * The ID of the Pub/Sub event handler interceptor, that can passed to the `UseEventHandler` decorator.
19
18
  */
@@ -86,6 +85,7 @@ let PubSubEventHandlerInterceptor = PubSubEventHandlerInterceptor_1 = class PubS
86
85
  constructor(serializer, reflector, logger) {
87
86
  super(PUBSUB_EVENT_HANDLER_ID, reflector, logger);
88
87
  this.serializer = serializer;
88
+ this.logger.setContext(PubSubEventHandlerInterceptor_1.name);
89
89
  }
90
90
  /**
91
91
  * Parses the given request as the payload of a Pub/Sub push request.
@@ -148,7 +148,7 @@ let PubSubEventHandlerInterceptor = PubSubEventHandlerInterceptor_1 = class PubS
148
148
  };
149
149
  PubSubEventHandlerInterceptorWithSerializer = __decorate([
150
150
  Injectable(),
151
- __metadata("design:paramtypes", [Reflector, PinoLogger])
151
+ __metadata("design:paramtypes", [Reflector, Logger])
152
152
  ], PubSubEventHandlerInterceptorWithSerializer);
153
153
  return PubSubEventHandlerInterceptorWithSerializer;
154
154
  }
@@ -156,6 +156,6 @@ let PubSubEventHandlerInterceptor = PubSubEventHandlerInterceptor_1 = class PubS
156
156
  PubSubEventHandlerInterceptor = PubSubEventHandlerInterceptor_1 = __decorate([
157
157
  Injectable(),
158
158
  __metadata("design:paramtypes", [Object, Reflector,
159
- PinoLogger])
159
+ Logger])
160
160
  ], PubSubEventHandlerInterceptor);
161
161
  export { PubSubEventHandlerInterceptor };
@@ -110,29 +110,30 @@ export class PubSubPublisher {
110
110
  ? topicOrPreparedEvent
111
111
  : await this.prepare(topicOrPreparedEvent, event, options);
112
112
  const pubSubTopic = this.getTopic(topic);
113
- const baseLogData = {
113
+ const messageInfo = {
114
114
  topic,
115
115
  pubSubTopic: pubSubTopic.name,
116
116
  };
117
117
  if (attributes && 'eventId' in attributes) {
118
- baseLogData.eventId = attributes.eventId;
118
+ messageInfo.eventId = attributes.eventId;
119
119
  }
120
120
  try {
121
- const pubSubMessageId = await pubSubTopic.publishMessage({
121
+ const messageId = await pubSubTopic.publishMessage({
122
122
  data,
123
123
  attributes,
124
124
  orderingKey: key,
125
125
  });
126
- this.logger.info({ ...baseLogData, pubSubMessageId }, 'Published message to Pub/Sub.');
126
+ this.logger.info({ publishedMessage: { ...messageInfo, messageId } }, 'Published message to Pub/Sub.');
127
127
  }
128
128
  catch (error) {
129
129
  this.logger.error({
130
- ...baseLogData,
131
- pubSubMessage: data.toString('base64'),
132
- pubSubAttributes: attributes,
133
- pubSubOrderingKey: key,
134
- errorMessage: error.message,
135
- errorStack: error.stack,
130
+ failedMessage: {
131
+ ...messageInfo,
132
+ data: data.toString('base64'),
133
+ attributes,
134
+ key,
135
+ },
136
+ error: error.stack,
136
137
  }, 'Failed to publish message to Pub/Sub.');
137
138
  throw error;
138
139
  }
@@ -1,3 +1,4 @@
1
+ import { Logger } from '@causa/runtime/nestjs';
1
2
  import { Database, Spanner } from '@google-cloud/spanner';
2
3
  import { type OnApplicationShutdown } from '@nestjs/common';
3
4
  /**
@@ -7,6 +8,7 @@ import { type OnApplicationShutdown } from '@nestjs/common';
7
8
  export declare class SpannerLifecycleService implements OnApplicationShutdown {
8
9
  private readonly spanner;
9
10
  private readonly database;
10
- constructor(spanner: Spanner, database: Database);
11
+ private readonly logger;
12
+ constructor(spanner: Spanner, database: Database, logger: Logger);
11
13
  onApplicationShutdown(): Promise<void>;
12
14
  }
@@ -7,7 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
+ import { Logger } from '@causa/runtime/nestjs';
10
11
  import { Database, Spanner } from '@google-cloud/spanner';
12
+ import { SessionLeakError } from '@google-cloud/spanner/build/src/session-pool.js';
11
13
  import { Injectable } from '@nestjs/common';
12
14
  /**
13
15
  * A private service that handles the graceful shutdown of the Spanner Database.
@@ -16,18 +18,29 @@ import { Injectable } from '@nestjs/common';
16
18
  let SpannerLifecycleService = class SpannerLifecycleService {
17
19
  spanner;
18
20
  database;
19
- constructor(spanner, database) {
21
+ logger;
22
+ constructor(spanner, database, logger) {
20
23
  this.spanner = spanner;
21
24
  this.database = database;
25
+ this.logger = logger;
22
26
  }
23
27
  async onApplicationShutdown() {
24
- await this.database.close();
25
- this.spanner.close();
28
+ try {
29
+ await this.database.close();
30
+ this.spanner.close();
31
+ }
32
+ catch (error) {
33
+ this.logger.error({
34
+ error: error.stack,
35
+ spannerLeaks: error instanceof SessionLeakError ? error.messages : undefined,
36
+ }, 'Failed to close Spanner client.');
37
+ }
26
38
  }
27
39
  };
28
40
  SpannerLifecycleService = __decorate([
29
41
  Injectable(),
30
42
  __metadata("design:paramtypes", [Spanner,
31
- Database])
43
+ Database,
44
+ Logger])
32
45
  ], SpannerLifecycleService);
33
46
  export { SpannerLifecycleService };
@@ -1,5 +1,6 @@
1
1
  import { EVENT_PUBLISHER_INJECTION_NAME, Logger } from '@causa/runtime/nestjs';
2
2
  import { ConfigService } from '@nestjs/config';
3
+ import { PubSubPublisher } from '../../pubsub/index.js';
3
4
  import { SpannerEntityManager } from '../../spanner/index.js';
4
5
  import { SpannerOutboxEvent } from './event.js';
5
6
  import { SpannerOutboxTransactionRunner } from './runner.js';
@@ -7,11 +8,12 @@ import { SpannerOutboxSender, } from './sender.js';
7
8
  /**
8
9
  * Combines options passed to the module with the configuration (from the environment).
9
10
  *
10
- * @param options Options passed to the module.
11
+ * @param defaultOptions Options inferred by the module itself.
12
+ * @param customOptions Options passed to the module by the caller.
11
13
  * @param config The {@link ConfigService} to use.
12
14
  * @returns The parsed {@link SpannerOutboxSenderOptions}.
13
15
  */
14
- function parseSenderOptions(options, config) {
16
+ function parseSenderOptions(defaultOptions, customOptions, config) {
15
17
  function validateIntOrUndefined(name) {
16
18
  const strValue = config.get(name);
17
19
  if (strValue === undefined) {
@@ -34,7 +36,7 @@ function parseSenderOptions(options, config) {
34
36
  const sharding = shardingColumn && shardingCount
35
37
  ? { column: shardingColumn, count: shardingCount }
36
38
  : undefined;
37
- const envOptions = {
39
+ const envOptionsWithUndefined = {
38
40
  batchSize,
39
41
  pollingInterval,
40
42
  idColumn,
@@ -43,8 +45,15 @@ function parseSenderOptions(options, config) {
43
45
  sharding,
44
46
  leaseDuration,
45
47
  };
46
- return { ...envOptions, ...options };
48
+ const envOptions = Object.fromEntries(Object.entries(envOptionsWithUndefined).filter(([, v]) => v !== undefined));
49
+ return { ...defaultOptions, ...envOptions, ...customOptions };
47
50
  }
51
+ /**
52
+ * The default lease duration, in milliseconds, when the publisher is Pub/Sub.
53
+ * Pub/Sub has a default retry/timeout of 60 seconds. This ensures the lease is long enough and the event doesn't get
54
+ * picked up by another instance.
55
+ */
56
+ const DEFAULT_PUBSUB_LEASE_DURATION = 70000;
48
57
  /**
49
58
  * The module providing the {@link SpannerOutboxTransactionRunner}.
50
59
  * This assumes the `SpannerModule` and an {@link EventPublisher} are available (as well as the `LoggerModule`).
@@ -69,7 +78,10 @@ export class SpannerOutboxTransactionModule {
69
78
  {
70
79
  provide: SpannerOutboxSender,
71
80
  useFactory: (entityManager, publisher, logger, config) => {
72
- const options = parseSenderOptions(senderOptions, config);
81
+ const defaultOptions = publisher instanceof PubSubPublisher
82
+ ? { leaseDuration: DEFAULT_PUBSUB_LEASE_DURATION }
83
+ : {};
84
+ const options = parseSenderOptions(defaultOptions, senderOptions, config);
73
85
  return new SpannerOutboxSender(entityManager, outboxEventType, publisher, logger, options);
74
86
  },
75
87
  inject: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@causa/runtime-google",
3
- "version": "0.35.0",
3
+ "version": "0.35.2",
4
4
  "description": "An extension to the Causa runtime SDK (`@causa/runtime`), providing Google-specific features.",
5
5
  "repository": "github:causa-io/runtime-typescript-google",
6
6
  "license": "ISC",
@@ -29,12 +29,12 @@
29
29
  "test:cov": "npm run test -- --coverage"
30
30
  },
31
31
  "dependencies": {
32
- "@causa/runtime": ">= 0.24.1 < 1.0.0",
32
+ "@causa/runtime": ">= 0.24.2 < 1.0.0",
33
33
  "@google-cloud/precise-date": "^4.0.0",
34
34
  "@google-cloud/pubsub": "^4.9.0",
35
35
  "@google-cloud/spanner": "^7.16.0",
36
36
  "@google-cloud/tasks": "^5.5.1",
37
- "@grpc/grpc-js": "^1.12.2",
37
+ "@grpc/grpc-js": "^1.12.3",
38
38
  "@nestjs/common": "^10.4.12",
39
39
  "@nestjs/config": "^3.3.0",
40
40
  "@nestjs/core": "^10.4.12",
@@ -45,7 +45,6 @@
45
45
  "express": "^4.21.1",
46
46
  "firebase-admin": "^13.0.1",
47
47
  "jsonwebtoken": "^9.0.2",
48
- "nestjs-pino": "^4.1.0",
49
48
  "passport-http-bearer": "^1.0.1",
50
49
  "pino": "^9.5.0",
51
50
  "reflect-metadata": "^0.2.2"
@@ -61,8 +60,8 @@
61
60
  "@types/passport-http-bearer": "^1.0.41",
62
61
  "@types/supertest": "^6.0.2",
63
62
  "@types/uuid": "^10.0.0",
64
- "dotenv": "^16.4.5",
65
- "eslint": "^9.15.0",
63
+ "dotenv": "^16.4.6",
64
+ "eslint": "^9.16.0",
66
65
  "eslint-config-prettier": "^9.1.0",
67
66
  "eslint-plugin-prettier": "^5.2.1",
68
67
  "jest": "^29.7.0",
@@ -72,7 +71,7 @@
72
71
  "ts-jest": "^29.2.5",
73
72
  "ts-node": "^10.9.2",
74
73
  "typescript": "^5.7.2",
75
- "typescript-eslint": "^8.16.0",
74
+ "typescript-eslint": "^8.17.0",
76
75
  "uuid": "^11.0.3"
77
76
  }
78
77
  }