node-server-engine 1.10.11 → 1.10.13
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 +117 -18
- package/dist/const/metrics.js +7 -0
- package/dist/const/metrics.js.map +1 -1
- package/dist/entities/PubSub/PubSub.d.ts +4 -2
- package/dist/entities/PubSub/PubSub.js +44 -5
- package/dist/entities/PubSub/PubSub.js.map +1 -1
- package/dist/entities/PubSub/PubSub.types.d.ts +34 -0
- package/dist/entities/PubSub/PubSub.types.js.map +1 -1
- package/dist/entities/PubSub/handleMessage/handleMessage.js +22 -4
- package/dist/entities/PubSub/handleMessage/handleMessage.js.map +1 -1
- package/dist/entities/PushNotification/PushNotification.d.ts +6 -2
- package/dist/entities/PushNotification/PushNotification.js +45 -9
- package/dist/entities/PushNotification/PushNotification.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -852,7 +852,9 @@ It can be configured through environment variables
|
|
|
852
852
|
|
|
853
853
|
### Pub/Sub
|
|
854
854
|
|
|
855
|
-
The engine exposes a `PubSub` entity that can be used to communicate with Google Cloud Pub/Sub.
|
|
855
|
+
The engine exposes a `PubSub` entity that can be used to communicate with Google Cloud Pub/Sub with production-ready configuration including flow control, retry policies, and batch processing.
|
|
856
|
+
|
|
857
|
+
Based on [@google-cloud/pubsub](https://github.com/googleapis/nodejs-pubsub) v4.11.0.
|
|
856
858
|
|
|
857
859
|
```javascript
|
|
858
860
|
import { PubSub } from 'node-server-engine';
|
|
@@ -861,32 +863,55 @@ import { PubSub } from 'node-server-engine';
|
|
|
861
863
|
* Declare that the service will be publishing to a topic
|
|
862
864
|
* This must be done before init() is called
|
|
863
865
|
* Any attempt to publish a message without declaring a topic first will fail
|
|
864
|
-
* @
|
|
866
|
+
* @param {string|Array<string>} topic - The topic(s) to which we will be publishing
|
|
867
|
+
* @param {Object} [options] - Publisher configuration options
|
|
865
868
|
*/
|
|
866
|
-
PubSub.addPublisher(topic
|
|
869
|
+
PubSub.addPublisher(topic, {
|
|
870
|
+
enableMessageOrdering: false, // Enable ordering (requires orderingKey when publishing)
|
|
871
|
+
batching: {
|
|
872
|
+
maxMessages: 100, // Max messages per batch
|
|
873
|
+
maxBytes: 1024 * 1024, // Max bytes per batch (1MB)
|
|
874
|
+
maxMilliseconds: 100 // Max delay before sending batch
|
|
875
|
+
},
|
|
876
|
+
retry: {
|
|
877
|
+
initialDelayMillis: 100, // Initial retry delay
|
|
878
|
+
maxDelayMillis: 60000, // Max retry delay (60s)
|
|
879
|
+
delayMultiplier: 1.3 // Exponential backoff multiplier
|
|
880
|
+
}
|
|
881
|
+
});
|
|
867
882
|
|
|
868
883
|
/**
|
|
869
|
-
* Binds a message
|
|
884
|
+
* Binds a message handler to a subscription
|
|
870
885
|
* If called multiple times, handlers will be chained
|
|
871
886
|
* This must be done before init() is called
|
|
872
887
|
* The subscription will not be consumed until init() is called
|
|
873
|
-
* @
|
|
874
|
-
* @
|
|
875
|
-
* @
|
|
888
|
+
* @param {string} subscription - The subscription to consume
|
|
889
|
+
* @param {function|Array<function>} handler - The message handling function(s)
|
|
890
|
+
* @param {Object} [options] - Subscriber configuration options
|
|
876
891
|
*/
|
|
877
|
-
PubSub.addSubscriber(subscription, handler,
|
|
892
|
+
PubSub.addSubscriber(subscription, handler, {
|
|
893
|
+
first: false, // Put handler at beginning of chain
|
|
894
|
+
isDebezium: false, // Handle Debezium CDC events
|
|
895
|
+
ackDeadline: 60, // Acknowledgement deadline (10-600 seconds)
|
|
896
|
+
flowControl: {
|
|
897
|
+
maxMessages: 1000, // Max concurrent messages
|
|
898
|
+
maxBytes: 100 * 1024 * 1024, // Max concurrent bytes (100MB)
|
|
899
|
+
allowExcessMessages: true // Allow excess messages if under maxBytes
|
|
900
|
+
}
|
|
901
|
+
});
|
|
878
902
|
|
|
879
903
|
/**
|
|
880
904
|
* Establish connection with all the declared publishers/subscribers
|
|
905
|
+
* Validates topic/subscription existence and permissions
|
|
881
906
|
*/
|
|
882
907
|
await PubSub.init();
|
|
883
908
|
|
|
884
909
|
/**
|
|
885
910
|
* Send a message through a previously declared publisher
|
|
886
|
-
* @
|
|
887
|
-
* @
|
|
888
|
-
* @
|
|
889
|
-
* @
|
|
911
|
+
* @param {string} topic - The name of the topic to which the message should be pushed
|
|
912
|
+
* @param {Object} message - The actual message (will be JSON stringified)
|
|
913
|
+
* @param {Object} [attributes] - Message attributes for filtering
|
|
914
|
+
* @param {string} [orderingKey] - Enforce ordering for messages with same key
|
|
890
915
|
*/
|
|
891
916
|
await PubSub.publish(topic, message, attributes, orderingKey);
|
|
892
917
|
|
|
@@ -896,28 +921,102 @@ await PubSub.publish(topic, message, attributes, orderingKey);
|
|
|
896
921
|
await PubSub.shutdown();
|
|
897
922
|
```
|
|
898
923
|
|
|
924
|
+
#### Message Handling
|
|
925
|
+
|
|
926
|
+
Messages are acknowledged after successful processing. If any handler throws an error, the message is nacked and will be redelivered according to the subscription's retry policy.
|
|
927
|
+
|
|
928
|
+
```javascript
|
|
929
|
+
// Handler signature
|
|
930
|
+
async function messageHandler(payload, attributes, publishedAt) {
|
|
931
|
+
// payload: The JSON message content
|
|
932
|
+
// attributes: Message attributes (key-value pairs)
|
|
933
|
+
// publishedAt: Date when message was published
|
|
934
|
+
|
|
935
|
+
// Process the message
|
|
936
|
+
await processData(payload);
|
|
937
|
+
|
|
938
|
+
// Message will be ack'd automatically after successful processing
|
|
939
|
+
// If an error is thrown, message will be nack'd for redelivery
|
|
940
|
+
}
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
#### Best Practices
|
|
944
|
+
|
|
945
|
+
1. **Flow Control**: Configure `flowControl` based on your service's memory and processing capacity
|
|
946
|
+
2. **Batch Settings**: Tune batching for optimal throughput vs. latency tradeoff
|
|
947
|
+
3. **Retry Policy**: Use exponential backoff to handle transient failures gracefully
|
|
948
|
+
4. **Dead Letter Topics**: Configure dead letter topics on your subscriptions in GCP Console for failed messages
|
|
949
|
+
5. **Exactly-Once Delivery**: Enable exactly-once delivery in GCP Console when creating/updating your subscription (requires ackWithResponse() in handlers)
|
|
950
|
+
6. **Message Ordering**: Only enable when strict ordering is required (reduces throughput)
|
|
951
|
+
7. **ACK Deadline**: Set based on your handler's processing time (default: 60s)
|
|
952
|
+
|
|
953
|
+
#### Environment Variables
|
|
954
|
+
|
|
955
|
+
Pub/Sub authentication uses [Google Cloud Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials). Set `GOOGLE_APPLICATION_CREDENTIALS` to your service account key path, or use Workload Identity in GKE.
|
|
956
|
+
|
|
899
957
|
---
|
|
900
958
|
|
|
901
959
|
### PushNotification
|
|
902
960
|
|
|
903
|
-
Communication interface with the push service.
|
|
961
|
+
Communication interface with the push notification service using Pub/Sub.
|
|
904
962
|
|
|
905
963
|
```javascript
|
|
906
964
|
import { PushNotification } from 'node-server-engine';
|
|
907
965
|
|
|
908
|
-
//
|
|
909
|
-
//
|
|
966
|
+
// Initialize with optimized settings for push notifications
|
|
967
|
+
// Must be called before sending notifications
|
|
910
968
|
await PushNotification.init();
|
|
911
969
|
|
|
912
970
|
/**
|
|
913
971
|
* Send a push notification through the push service
|
|
914
|
-
* @param {
|
|
972
|
+
* @param {string} userId - ID of the user that should receive the notification
|
|
915
973
|
* @param {Object} notification - Notification that should be sent
|
|
916
|
-
* @
|
|
974
|
+
* @throws {EngineError} If userId is missing or publish fails
|
|
917
975
|
*/
|
|
918
|
-
await PushNotification.sendPush(userId,
|
|
976
|
+
await PushNotification.sendPush(userId, {
|
|
977
|
+
title: 'Hello!',
|
|
978
|
+
body: 'You have a new message',
|
|
979
|
+
payload: { type: 'message', messageId: '123' },
|
|
980
|
+
priority: true,
|
|
981
|
+
ttl: 3600 // Time to live in seconds
|
|
982
|
+
});
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
#### Notification Options
|
|
986
|
+
|
|
987
|
+
```typescript
|
|
988
|
+
{
|
|
989
|
+
title?: string; // Notification title
|
|
990
|
+
body?: string; // Notification body text
|
|
991
|
+
payload?: unknown; // Custom data payload
|
|
992
|
+
voip?: boolean; // VOIP notification (iOS)
|
|
993
|
+
background?: boolean; // Background/data-only notification
|
|
994
|
+
token?: string; // Specific device token (optional)
|
|
995
|
+
mutable?: boolean; // Content can be mutated by client (iOS)
|
|
996
|
+
contentAvailable?: boolean; // Requires client-side processing (iOS)
|
|
997
|
+
ttl?: number; // Time to live in seconds
|
|
998
|
+
priority?: boolean; // High priority notification
|
|
999
|
+
collapseId?: string; // Group notifications with same ID
|
|
1000
|
+
}
|
|
919
1001
|
```
|
|
920
1002
|
|
|
1003
|
+
#### Environment Variables
|
|
1004
|
+
|
|
1005
|
+
| Variable | Description | Required |
|
|
1006
|
+
| -------- | ----------- | -------- |
|
|
1007
|
+
| `PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC` | Pub/Sub topic name for push notifications | ✓ |
|
|
1008
|
+
|
|
1009
|
+
#### Configuration
|
|
1010
|
+
|
|
1011
|
+
The PushNotification entity is pre-configured with optimal settings:
|
|
1012
|
+
- **Batching**: Up to 100 messages, 1MB max, 50ms delay for fast delivery
|
|
1013
|
+
- **Retry**: Exponential backoff with 100ms initial delay, up to 60s max delay
|
|
1014
|
+
- **No Ordering**: Push notifications don't require strict ordering for better throughput
|
|
1015
|
+
|
|
1016
|
+
#### Integration
|
|
1017
|
+
|
|
1018
|
+
Your `omg-notification-service` (or similar) should subscribe to the configured topic to process and deliver push notifications to devices via FCM, APNs, or other push providers.
|
|
1019
|
+
|
|
921
1020
|
---
|
|
922
1021
|
|
|
923
1022
|
### Localizator
|
package/dist/const/metrics.js
CHANGED
|
@@ -7,6 +7,13 @@ exports.serviceRegistry = exports.instanceRegistry = void 0;
|
|
|
7
7
|
var _promClient = require("prom-client");
|
|
8
8
|
// Registry for metrics that apply to this specific instance of the service
|
|
9
9
|
const instanceRegistry = exports.instanceRegistry = new _promClient.Registry();
|
|
10
|
+
|
|
10
11
|
// Registry for metrics that apply to the service as a whole
|
|
11
12
|
const serviceRegistry = exports.serviceRegistry = new _promClient.Registry();
|
|
13
|
+
|
|
14
|
+
// Collect default Node.js process metrics (includes process_start_time_seconds)
|
|
15
|
+
(0, _promClient.collectDefaultMetrics)({
|
|
16
|
+
register: instanceRegistry,
|
|
17
|
+
prefix: ''
|
|
18
|
+
});
|
|
12
19
|
//# sourceMappingURL=metrics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","names":["_promClient","require","instanceRegistry","exports","Registry","serviceRegistry"],"sources":["../../src/const/metrics.ts"],"sourcesContent":["import { Registry } from 'prom-client';\n\n// Registry for metrics that apply to this specific instance of the service\nexport const instanceRegistry = new Registry();\n// Registry for metrics that apply to the service as a whole\nexport const serviceRegistry = new Registry();\n"],"mappings":";;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAEA;AACO,MAAMC,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG,IAAIE,oBAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"metrics.js","names":["_promClient","require","instanceRegistry","exports","Registry","serviceRegistry","collectDefaultMetrics","register","prefix"],"sources":["../../src/const/metrics.ts"],"sourcesContent":["import { Registry, collectDefaultMetrics } from 'prom-client';\n\n// Registry for metrics that apply to this specific instance of the service\nexport const instanceRegistry = new Registry();\n\n// Registry for metrics that apply to the service as a whole\nexport const serviceRegistry = new Registry();\n\n// Collect default Node.js process metrics (includes process_start_time_seconds)\ncollectDefaultMetrics({\n register: instanceRegistry,\n prefix: '',\n});\n"],"mappings":";;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAEA;AACO,MAAMC,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG,IAAIE,oBAAQ,CAAC,CAAC;;AAE9C;AACO,MAAMC,eAAe,GAAAF,OAAA,CAAAE,eAAA,GAAG,IAAID,oBAAQ,CAAC,CAAC;;AAE7C;AACA,IAAAE,iCAAqB,EAAC;EACpBC,QAAQ,EAAEL,gBAAgB;EAC1BM,MAAM,EAAE;AACV,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Attributes } from '@google-cloud/pubsub';
|
|
2
|
-
import { PubSubAddSubscriberOptions, PubSubMessageHandler } from './PubSub.types';
|
|
2
|
+
import { PubSubAddSubscriberOptions, PubSubMessageHandler, PubSubPublisherOptions } from './PubSub.types';
|
|
3
3
|
/**
|
|
4
4
|
* This is a singleton service who's role is to manage pub/sub connections
|
|
5
5
|
* It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects
|
|
@@ -16,8 +16,10 @@ export declare const PubSub: {
|
|
|
16
16
|
shutdown(): Promise<void>;
|
|
17
17
|
/**
|
|
18
18
|
* Creates a publisher if it does not exist yet
|
|
19
|
+
* @param topic - Topic name or array of topic names
|
|
20
|
+
* @param options - Publisher configuration options
|
|
19
21
|
*/
|
|
20
|
-
addPublisher(topic: string | Array<string
|
|
22
|
+
addPublisher(topic: string | Array<string>, options?: PubSubPublisherOptions): void;
|
|
21
23
|
/**
|
|
22
24
|
* Add message handlers to a subscription
|
|
23
25
|
* @param subscription - Name of the Pub/Sub subscription
|
|
@@ -93,18 +93,43 @@ const PubSub = exports.PubSub = {
|
|
|
93
93
|
},
|
|
94
94
|
/**
|
|
95
95
|
* Creates a publisher if it does not exist yet
|
|
96
|
+
* @param topic - Topic name or array of topic names
|
|
97
|
+
* @param options - Publisher configuration options
|
|
96
98
|
*/
|
|
97
|
-
addPublisher(topic) {
|
|
99
|
+
addPublisher(topic, options) {
|
|
98
100
|
const topicArray = topic instanceof Array ? topic : [topic];
|
|
99
101
|
(0, _utils.reportDebug)({
|
|
100
102
|
namespace,
|
|
101
|
-
message: `Registering publisher for topics '${topicArray.join("', '")}'
|
|
103
|
+
message: `Registering publisher for topics '${topicArray.join("', '")}'`,
|
|
104
|
+
data: options
|
|
102
105
|
});
|
|
103
106
|
// Register a new topic if it does not exist yet
|
|
104
107
|
for (const topic of topicArray) {
|
|
105
108
|
if (!Object.keys(topics).includes(topic)) {
|
|
106
109
|
topics[topic] = pubSubClient.topic(topic, {
|
|
107
|
-
enableOpenTelemetryTracing: true
|
|
110
|
+
enableOpenTelemetryTracing: true,
|
|
111
|
+
messageOrdering: options?.enableMessageOrdering ?? false,
|
|
112
|
+
batching: options?.batching ? {
|
|
113
|
+
maxMessages: options.batching.maxMessages ?? 100,
|
|
114
|
+
maxBytes: options.batching.maxBytes ?? 1024 * 1024,
|
|
115
|
+
// 1MB
|
|
116
|
+
maxMilliseconds: options.batching.maxMilliseconds ?? 100
|
|
117
|
+
} : undefined,
|
|
118
|
+
gaxOpts: options?.retry ? {
|
|
119
|
+
retry: {
|
|
120
|
+
retryCodes: [10, 14],
|
|
121
|
+
// ABORTED, UNAVAILABLE
|
|
122
|
+
backoffSettings: {
|
|
123
|
+
initialRetryDelayMillis: options.retry.initialDelayMillis ?? 100,
|
|
124
|
+
maxRetryDelayMillis: options.retry.maxDelayMillis ?? 60000,
|
|
125
|
+
retryDelayMultiplier: options.retry.delayMultiplier ?? 1.3,
|
|
126
|
+
initialRpcTimeoutMillis: 60000,
|
|
127
|
+
maxRpcTimeoutMillis: 600000,
|
|
128
|
+
rpcTimeoutMultiplier: 1.0,
|
|
129
|
+
totalTimeoutMillis: 600000
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} : undefined
|
|
108
133
|
});
|
|
109
134
|
}
|
|
110
135
|
}
|
|
@@ -119,19 +144,33 @@ const PubSub = exports.PubSub = {
|
|
|
119
144
|
const handlers = handler instanceof Array ? handler : [handler];
|
|
120
145
|
const isDebezium = options?.isDebezium ?? false;
|
|
121
146
|
const first = options?.first ?? false;
|
|
147
|
+
const flowControl = options?.flowControl;
|
|
148
|
+
const ackDeadline = options?.ackDeadline;
|
|
122
149
|
(0, _utils.reportDebug)({
|
|
123
150
|
namespace,
|
|
124
151
|
message: `Registering subscriber for subscription '${subscription}'`,
|
|
125
152
|
data: {
|
|
126
153
|
first,
|
|
127
154
|
handlersCount: handlers.length,
|
|
128
|
-
isDebezium
|
|
155
|
+
isDebezium,
|
|
156
|
+
flowControl
|
|
129
157
|
}
|
|
130
158
|
});
|
|
131
159
|
if (!Object.keys(subscriptions).includes(subscription)) {
|
|
132
160
|
subscriptions[subscription] = {
|
|
133
161
|
subscription: pubSubClient.subscription(subscription, {
|
|
134
|
-
enableOpenTelemetryTracing: true
|
|
162
|
+
enableOpenTelemetryTracing: true,
|
|
163
|
+
flowControl: flowControl ? {
|
|
164
|
+
maxMessages: flowControl.maxMessages ?? 1000,
|
|
165
|
+
maxBytes: flowControl.maxBytes ?? 100 * 1024 * 1024,
|
|
166
|
+
// 100MB
|
|
167
|
+
allowExcessMessages: flowControl.allowExcessMessages ?? true
|
|
168
|
+
} : {
|
|
169
|
+
maxMessages: 1000,
|
|
170
|
+
maxBytes: 100 * 1024 * 1024,
|
|
171
|
+
allowExcessMessages: true
|
|
172
|
+
},
|
|
173
|
+
ackDeadline: ackDeadline ?? 60
|
|
135
174
|
}),
|
|
136
175
|
handlers: []
|
|
137
176
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PubSub.js","names":["_pubsub","require","_handleMessage","_EngineError","_LifecycleController","_utils","namespace","subscriptions","topics","pubSubClient","PubsubClient","PubSub","exports","init","reportDebug","message","Promise","all","Object","values","map","topic","exists","EngineError","name","permissions","iam","testPermissions","hasPermissions","every","success","subscription","handlers","isDebezium","on","handleMessage","catch","error","reportError","LifecycleController","register","shutdown","flush","close","keys","forEach","addPublisher","topicArray","Array","join","includes","enableOpenTelemetryTracing","addSubscriber","handler","options","first","data","handlersCount","length","push","unshift","publish","attributes","orderingKey","publishMessage","json"],"sources":["../../../src/entities/PubSub/PubSub.ts"],"sourcesContent":["import {\n Attributes,\n PubSub as PubsubClient,\n Message\n} from '@google-cloud/pubsub';\nimport { handleMessage } from './handleMessage';\nimport {\n PubSubTopicsMap,\n PubSubSubscriptionsMap,\n PubSubAddSubscriberOptions,\n PubSubMessageHandler\n} from './PubSub.types';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PubSub';\n\n/**\n * Associative containing all the subscriptions sorted by topic registered for publishing\n */\nconst subscriptions: PubSubSubscriptionsMap = {};\n\n/**\n * Associative containing all the topics registered for publishing\n */\nconst topics: PubSubTopicsMap = {};\n\nconst pubSubClient = new PubsubClient();\n\n/**\n * This is a singleton service who's role is to manage pub/sub connections\n * It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects\n * It avoids potential subscription splitting on an instance\n */\nexport const PubSub = {\n /**\n * Initialize the registered publishers and subscribers\n */\n async init(): Promise<void> {\n reportDebug({ namespace, message: `Initializing Pub/Sub` });\n // Check that all topics/permissions exist, and that we have permission on them\n await Promise.all([\n // Test that all topics actually exist and that we have permissions\n ...Object.values(topics).map(async (topic) => {\n const [exists] = await topic.exists();\n if (!exists)\n throw new EngineError(`Topic ${topic.name} does not exist`);\n // Test permission ton publish to topic\n const [permissions] = await topic.iam.testPermissions([\n 'pubsub.topics.publish'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to publish to topic ${topic.name}`\n );\n }),\n // Test that all subscriptions actually exist and that we have permissions\n ...Object.values(subscriptions).map(async ({ subscription }) => {\n const [exists] = await subscription.exists();\n if (!exists)\n throw new EngineError(\n `Subscription ${subscription.name} does not exist`\n );\n // Test permission ton consume subscription\n const [permissions] = await subscription.iam.testPermissions([\n 'pubsub.subscriptions.consume'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to subscribe to subscription ${subscription.name}`\n );\n })\n ]);\n\n // Register handlers on the subscriptions to activate them\n for (const { subscription, handlers, isDebezium } of Object.values(\n subscriptions\n )) {\n subscription.on('message', (message) => {\n handleMessage(\n message as Message,\n subscription,\n handlers,\n isDebezium\n ).catch((error) => {\n reportError(error);\n });\n });\n }\n\n LifecycleController.register(PubSub);\n },\n\n /**\n * Shutdown the registered publishers and subscribers\n */\n async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Pub/Sub` });\n // Flush all pending messages and shut down connections\n await Promise.all([\n ...Object.values(topics).map(async (topic) => topic.flush()),\n ...Object.values(subscriptions).map(async ({ subscription }) =>\n subscription.close()\n )\n ]);\n // Delete the stored topics/subscriptions\n Object.keys(topics).forEach((topic) => delete topics[topic]);\n Object.keys(subscriptions).forEach(\n (subscription) => delete subscriptions[subscription]\n );\n // Close the client\n await pubSubClient.close();\n },\n\n /**\n * Creates a publisher if it does not exist yet\n */\n addPublisher(topic: string | Array<string>): void {\n const topicArray = topic instanceof Array ? topic : [topic];\n reportDebug({\n namespace,\n message: `Registering publisher for topics '${topicArray.join(\"', '\")}'`\n });\n // Register a new topic if it does not exist yet\n for (const topic of topicArray) {\n if (!Object.keys(topics).includes(topic)) {\n topics[topic] = pubSubClient.topic(topic, {\n enableOpenTelemetryTracing: true\n });\n }\n }\n },\n\n /**\n * Add message handlers to a subscription\n * @param subscription - Name of the Pub/Sub subscription\n * @param handler - Function or list of functions to set as message handlers\n * @param options - Indicates that the additional options need to handle on Subscriber\n */\n addSubscriber<T>(\n subscription: string,\n handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>,\n options: PubSubAddSubscriberOptions = {}\n ): void {\n const handlers = handler instanceof Array ? handler : [handler];\n const isDebezium = options?.isDebezium ?? false;\n const first = options?.first ?? false;\n\n reportDebug({\n namespace,\n message: `Registering subscriber for subscription '${subscription}'`,\n data: { first, handlersCount: handlers.length, isDebezium }\n });\n if (!Object.keys(subscriptions).includes(subscription)) {\n subscriptions[subscription] = {\n subscription: pubSubClient.subscription(subscription, {\n enableOpenTelemetryTracing: true\n }),\n handlers: []\n };\n }\n\n if (isDebezium) subscriptions[subscription].isDebezium = true;\n\n if (first) subscriptions[subscription].handlers.push(...handlers);\n else subscriptions[subscription].handlers.unshift(...handlers);\n },\n\n /**\n * Send a message to a Pub/Sub topic\n * @param topic - Name of the topic\n * @param message - Content of the message, must be an object that can be JSON stringified\n * @param attributes - Attributes for this message, can be used to filter messages on the subscription\n * @param orderingKey - Enforce order guarantee of messages sent with the same key\n */\n async publish(\n topic: string,\n message: unknown,\n attributes?: Attributes,\n orderingKey?: string\n ): Promise<void> {\n if (!topics[topic])\n throw new EngineError(`No publisher was registered for topic '${topic}'`);\n reportDebug({\n namespace,\n message: `Sending message to topic '${topic}'`,\n data: { message, attributes, orderingKey }\n });\n await topics[topic].publishMessage({\n json: message,\n attributes,\n orderingKey\n });\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"PubSub.js","names":["_pubsub","require","_handleMessage","_EngineError","_LifecycleController","_utils","namespace","subscriptions","topics","pubSubClient","PubsubClient","PubSub","exports","init","reportDebug","message","Promise","all","Object","values","map","topic","exists","EngineError","name","permissions","iam","testPermissions","hasPermissions","every","success","subscription","handlers","isDebezium","on","handleMessage","catch","error","reportError","LifecycleController","register","shutdown","flush","close","keys","forEach","addPublisher","options","topicArray","Array","join","data","includes","enableOpenTelemetryTracing","messageOrdering","enableMessageOrdering","batching","maxMessages","maxBytes","maxMilliseconds","undefined","gaxOpts","retry","retryCodes","backoffSettings","initialRetryDelayMillis","initialDelayMillis","maxRetryDelayMillis","maxDelayMillis","retryDelayMultiplier","delayMultiplier","initialRpcTimeoutMillis","maxRpcTimeoutMillis","rpcTimeoutMultiplier","totalTimeoutMillis","addSubscriber","handler","first","flowControl","ackDeadline","handlersCount","length","allowExcessMessages","push","unshift","publish","attributes","orderingKey","publishMessage","json"],"sources":["../../../src/entities/PubSub/PubSub.ts"],"sourcesContent":["import {\n Attributes,\n PubSub as PubsubClient,\n Message\n} from '@google-cloud/pubsub';\nimport { handleMessage } from './handleMessage';\nimport {\n PubSubTopicsMap,\n PubSubSubscriptionsMap,\n PubSubAddSubscriberOptions,\n PubSubMessageHandler,\n PubSubPublisherOptions\n} from './PubSub.types';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PubSub';\n\n/**\n * Associative containing all the subscriptions sorted by topic registered for publishing\n */\nconst subscriptions: PubSubSubscriptionsMap = {};\n\n/**\n * Associative containing all the topics registered for publishing\n */\nconst topics: PubSubTopicsMap = {};\n\nconst pubSubClient = new PubsubClient();\n\n/**\n * This is a singleton service who's role is to manage pub/sub connections\n * It is the preferred way to interact with Pub/Sub as it manages the underlying subscription and handler objects\n * It avoids potential subscription splitting on an instance\n */\nexport const PubSub = {\n /**\n * Initialize the registered publishers and subscribers\n */\n async init(): Promise<void> {\n reportDebug({ namespace, message: `Initializing Pub/Sub` });\n // Check that all topics/permissions exist, and that we have permission on them\n await Promise.all([\n // Test that all topics actually exist and that we have permissions\n ...Object.values(topics).map(async (topic) => {\n const [exists] = await topic.exists();\n if (!exists)\n throw new EngineError(`Topic ${topic.name} does not exist`);\n // Test permission ton publish to topic\n const [permissions] = await topic.iam.testPermissions([\n 'pubsub.topics.publish'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to publish to topic ${topic.name}`\n );\n }),\n // Test that all subscriptions actually exist and that we have permissions\n ...Object.values(subscriptions).map(async ({ subscription }) => {\n const [exists] = await subscription.exists();\n if (!exists)\n throw new EngineError(\n `Subscription ${subscription.name} does not exist`\n );\n // Test permission ton consume subscription\n const [permissions] = await subscription.iam.testPermissions([\n 'pubsub.subscriptions.consume'\n ]);\n const hasPermissions = Object.values(permissions).every(\n (success) => success\n );\n if (!hasPermissions)\n throw new EngineError(\n `Instance does not have appropriate permissions to subscribe to subscription ${subscription.name}`\n );\n })\n ]);\n\n // Register handlers on the subscriptions to activate them\n for (const { subscription, handlers, isDebezium } of Object.values(\n subscriptions\n )) {\n subscription.on('message', (message) => {\n handleMessage(\n message as Message,\n subscription,\n handlers,\n isDebezium\n ).catch((error) => {\n reportError(error);\n });\n });\n }\n\n LifecycleController.register(PubSub);\n },\n\n /**\n * Shutdown the registered publishers and subscribers\n */\n async shutdown(): Promise<void> {\n reportDebug({ namespace, message: `Shutting down Pub/Sub` });\n // Flush all pending messages and shut down connections\n await Promise.all([\n ...Object.values(topics).map(async (topic) => topic.flush()),\n ...Object.values(subscriptions).map(async ({ subscription }) =>\n subscription.close()\n )\n ]);\n // Delete the stored topics/subscriptions\n Object.keys(topics).forEach((topic) => delete topics[topic]);\n Object.keys(subscriptions).forEach(\n (subscription) => delete subscriptions[subscription]\n );\n // Close the client\n await pubSubClient.close();\n },\n\n /**\n * Creates a publisher if it does not exist yet\n * @param topic - Topic name or array of topic names\n * @param options - Publisher configuration options\n */\n addPublisher(\n topic: string | Array<string>,\n options?: PubSubPublisherOptions\n ): void {\n const topicArray = topic instanceof Array ? topic : [topic];\n reportDebug({\n namespace,\n message: `Registering publisher for topics '${topicArray.join(\"', '\")}'`,\n data: options\n });\n // Register a new topic if it does not exist yet\n for (const topic of topicArray) {\n if (!Object.keys(topics).includes(topic)) {\n topics[topic] = pubSubClient.topic(topic, {\n enableOpenTelemetryTracing: true,\n messageOrdering: options?.enableMessageOrdering ?? false,\n batching: options?.batching\n ? {\n maxMessages: options.batching.maxMessages ?? 100,\n maxBytes: options.batching.maxBytes ?? 1024 * 1024, // 1MB\n maxMilliseconds: options.batching.maxMilliseconds ?? 100\n }\n : undefined,\n gaxOpts: options?.retry\n ? {\n retry: {\n retryCodes: [10, 14], // ABORTED, UNAVAILABLE\n backoffSettings: {\n initialRetryDelayMillis:\n options.retry.initialDelayMillis ?? 100,\n maxRetryDelayMillis: options.retry.maxDelayMillis ?? 60000,\n retryDelayMultiplier: options.retry.delayMultiplier ?? 1.3,\n initialRpcTimeoutMillis: 60000,\n maxRpcTimeoutMillis: 600000,\n rpcTimeoutMultiplier: 1.0,\n totalTimeoutMillis: 600000\n }\n }\n }\n : undefined\n });\n }\n }\n },\n\n /**\n * Add message handlers to a subscription\n * @param subscription - Name of the Pub/Sub subscription\n * @param handler - Function or list of functions to set as message handlers\n * @param options - Indicates that the additional options need to handle on Subscriber\n */\n addSubscriber<T>(\n subscription: string,\n handler: PubSubMessageHandler<T> | Array<PubSubMessageHandler<T>>,\n options: PubSubAddSubscriberOptions = {}\n ): void {\n const handlers = handler instanceof Array ? handler : [handler];\n const isDebezium = options?.isDebezium ?? false;\n const first = options?.first ?? false;\n const flowControl = options?.flowControl;\n const ackDeadline = options?.ackDeadline;\n\n reportDebug({\n namespace,\n message: `Registering subscriber for subscription '${subscription}'`,\n data: {\n first,\n handlersCount: handlers.length,\n isDebezium,\n flowControl\n }\n });\n if (!Object.keys(subscriptions).includes(subscription)) {\n subscriptions[subscription] = {\n subscription: pubSubClient.subscription(subscription, {\n enableOpenTelemetryTracing: true,\n flowControl: flowControl\n ? {\n maxMessages: flowControl.maxMessages ?? 1000,\n maxBytes: flowControl.maxBytes ?? 100 * 1024 * 1024, // 100MB\n allowExcessMessages: flowControl.allowExcessMessages ?? true\n }\n : {\n maxMessages: 1000,\n maxBytes: 100 * 1024 * 1024,\n allowExcessMessages: true\n },\n ackDeadline: ackDeadline ?? 60\n }),\n handlers: []\n };\n }\n\n if (isDebezium) subscriptions[subscription].isDebezium = true;\n\n if (first) subscriptions[subscription].handlers.push(...handlers);\n else subscriptions[subscription].handlers.unshift(...handlers);\n },\n\n /**\n * Send a message to a Pub/Sub topic\n * @param topic - Name of the topic\n * @param message - Content of the message, must be an object that can be JSON stringified\n * @param attributes - Attributes for this message, can be used to filter messages on the subscription\n * @param orderingKey - Enforce order guarantee of messages sent with the same key\n */\n async publish(\n topic: string,\n message: unknown,\n attributes?: Attributes,\n orderingKey?: string\n ): Promise<void> {\n if (!topics[topic])\n throw new EngineError(`No publisher was registered for topic '${topic}'`);\n reportDebug({\n namespace,\n message: `Sending message to topic '${topic}'`,\n data: { message, attributes, orderingKey }\n });\n await topics[topic].publishMessage({\n json: message,\n attributes,\n orderingKey\n });\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -21,6 +21,40 @@ export interface PubSubAddSubscriberOptions {
|
|
|
21
21
|
first?: boolean;
|
|
22
22
|
/** Indicates that the topic is a Debezium CDC topic, additional processing is done on the message */
|
|
23
23
|
isDebezium?: boolean;
|
|
24
|
+
/** Flow control settings for the subscriber */
|
|
25
|
+
flowControl?: {
|
|
26
|
+
/** Maximum number of messages to process concurrently */
|
|
27
|
+
maxMessages?: number;
|
|
28
|
+
/** Maximum bytes to process concurrently */
|
|
29
|
+
maxBytes?: number;
|
|
30
|
+
/** Allow more messages than maxMessages if total bytes is under maxBytes */
|
|
31
|
+
allowExcessMessages?: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Acknowledgement deadline in seconds (10-600) */
|
|
34
|
+
ackDeadline?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Options for configuring a publisher */
|
|
37
|
+
export interface PubSubPublisherOptions {
|
|
38
|
+
/** Enable message ordering (requires orderingKey when publishing) */
|
|
39
|
+
enableMessageOrdering?: boolean;
|
|
40
|
+
/** Batching settings */
|
|
41
|
+
batching?: {
|
|
42
|
+
/** Maximum messages to batch */
|
|
43
|
+
maxMessages?: number;
|
|
44
|
+
/** Maximum bytes to batch */
|
|
45
|
+
maxBytes?: number;
|
|
46
|
+
/** Maximum delay in milliseconds */
|
|
47
|
+
maxMilliseconds?: number;
|
|
48
|
+
};
|
|
49
|
+
/** Retry settings */
|
|
50
|
+
retry?: {
|
|
51
|
+
/** Initial retry delay in milliseconds */
|
|
52
|
+
initialDelayMillis?: number;
|
|
53
|
+
/** Maximum retry delay in milliseconds */
|
|
54
|
+
maxDelayMillis?: number;
|
|
55
|
+
/** Retry delay multiplier */
|
|
56
|
+
delayMultiplier?: number;
|
|
57
|
+
};
|
|
24
58
|
}
|
|
25
59
|
export type PubSubMessageHandler<T> = (message: T, attributes: Attributes, publishedAt: Date) => void | Promise<void>;
|
|
26
60
|
/** Debezium create event payload */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PubSub.types.js","names":[],"sources":["../../../src/entities/PubSub/PubSub.types.ts"],"sourcesContent":["import { Topic, Subscription, Attributes } from '@google-cloud/pubsub';\nimport { DebeziumEvent } from 'const/DebeziumEvent';\n\n/** List of all active topics */\nexport interface PubSubTopicsMap {\n [topic: string]: Topic;\n}\n\n/** List of subscriptions handled for each topic */\nexport interface PubSubSubscriptionsMap {\n [topic: string]: {\n /** The Pub/Sub subscription object */\n subscription: Subscription;\n /** The message handling functions */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handlers: Array<PubSubMessageHandler<any>>;\n /** Indicates that the subscription from Debezium events */\n isDebezium?: boolean;\n };\n}\n\n/** Options to setup a subscription */\nexport interface PubSubAddSubscriberOptions {\n /** Should the handlers declared here be added to the top of the queue, and called first when an new message arrives */\n first?: boolean;\n /** Indicates that the topic is a Debezium CDC topic, additional processing is done on the message */\n isDebezium?: boolean;\n}\n\nexport type PubSubMessageHandler<T> = (\n message: T,\n attributes: Attributes,\n publishedAt: Date\n) => void | Promise<void>;\n\n/** Debezium create event payload */\ninterface PubSubDebeziumCreatePayload<T> {\n /** The instance properties before the operation */\n before: null;\n /** The instance properties after the operation */\n after: T;\n /** The operation that was executed */\n op: DebeziumEvent.Create;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Debezium update event payload */\ninterface PubSubDebeziumUpdatePayload<T> {\n /** The instance properties before the operation */\n before: T;\n /** The instance properties after the operation */\n after: T;\n /** The operation that was executed */\n op: DebeziumEvent.Update;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Debezium delete event payload */\ninterface PubSubDebeziumDeletePayload<T> {\n /** The instance properties before the operation */\n before: T;\n /** The instance properties after the operation */\n after: null;\n /** The operation that was executed */\n op: DebeziumEvent.Delete;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Payload of Debezium messages */\nexport type PubSubDebeziumPayload<T> =\n | PubSubDebeziumCreatePayload<T>\n | PubSubDebeziumUpdatePayload<T>\n | PubSubDebeziumDeletePayload<T>;\n"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"PubSub.types.js","names":[],"sources":["../../../src/entities/PubSub/PubSub.types.ts"],"sourcesContent":["import { Topic, Subscription, Attributes } from '@google-cloud/pubsub';\nimport { DebeziumEvent } from 'const/DebeziumEvent';\n\n/** List of all active topics */\nexport interface PubSubTopicsMap {\n [topic: string]: Topic;\n}\n\n/** List of subscriptions handled for each topic */\nexport interface PubSubSubscriptionsMap {\n [topic: string]: {\n /** The Pub/Sub subscription object */\n subscription: Subscription;\n /** The message handling functions */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n handlers: Array<PubSubMessageHandler<any>>;\n /** Indicates that the subscription from Debezium events */\n isDebezium?: boolean;\n };\n}\n\n/** Options to setup a subscription */\nexport interface PubSubAddSubscriberOptions {\n /** Should the handlers declared here be added to the top of the queue, and called first when an new message arrives */\n first?: boolean;\n /** Indicates that the topic is a Debezium CDC topic, additional processing is done on the message */\n isDebezium?: boolean;\n /** Flow control settings for the subscriber */\n flowControl?: {\n /** Maximum number of messages to process concurrently */\n maxMessages?: number;\n /** Maximum bytes to process concurrently */\n maxBytes?: number;\n /** Allow more messages than maxMessages if total bytes is under maxBytes */\n allowExcessMessages?: boolean;\n };\n /** Acknowledgement deadline in seconds (10-600) */\n ackDeadline?: number;\n}\n\n/** Options for configuring a publisher */\nexport interface PubSubPublisherOptions {\n /** Enable message ordering (requires orderingKey when publishing) */\n enableMessageOrdering?: boolean;\n /** Batching settings */\n batching?: {\n /** Maximum messages to batch */\n maxMessages?: number;\n /** Maximum bytes to batch */\n maxBytes?: number;\n /** Maximum delay in milliseconds */\n maxMilliseconds?: number;\n };\n /** Retry settings */\n retry?: {\n /** Initial retry delay in milliseconds */\n initialDelayMillis?: number;\n /** Maximum retry delay in milliseconds */\n maxDelayMillis?: number;\n /** Retry delay multiplier */\n delayMultiplier?: number;\n };\n}\n\nexport type PubSubMessageHandler<T> = (\n message: T,\n attributes: Attributes,\n publishedAt: Date\n) => void | Promise<void>;\n\n/** Debezium create event payload */\ninterface PubSubDebeziumCreatePayload<T> {\n /** The instance properties before the operation */\n before: null;\n /** The instance properties after the operation */\n after: T;\n /** The operation that was executed */\n op: DebeziumEvent.Create;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Debezium update event payload */\ninterface PubSubDebeziumUpdatePayload<T> {\n /** The instance properties before the operation */\n before: T;\n /** The instance properties after the operation */\n after: T;\n /** The operation that was executed */\n op: DebeziumEvent.Update;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Debezium delete event payload */\ninterface PubSubDebeziumDeletePayload<T> {\n /** The instance properties before the operation */\n before: T;\n /** The instance properties after the operation */\n after: null;\n /** The operation that was executed */\n op: DebeziumEvent.Delete;\n /** List of fields that have changed */\n diff: Array<string>;\n}\n\n/** Payload of Debezium messages */\nexport type PubSubDebeziumPayload<T> =\n | PubSubDebeziumCreatePayload<T>\n | PubSubDebeziumUpdatePayload<T>\n | PubSubDebeziumDeletePayload<T>;\n"],"mappings":"","ignoreList":[]}
|
|
@@ -21,7 +21,6 @@ async function handleMessage(message, subscription, handlers, isDebezium = false
|
|
|
21
21
|
namespace,
|
|
22
22
|
message: `Received message from Pub/Sub [${subscription.name}]`
|
|
23
23
|
});
|
|
24
|
-
message.ack();
|
|
25
24
|
let payload;
|
|
26
25
|
try {
|
|
27
26
|
payload = JSON.parse(message.data.toString());
|
|
@@ -42,22 +41,41 @@ async function handleMessage(message, subscription, handlers, isDebezium = false
|
|
|
42
41
|
}
|
|
43
42
|
});
|
|
44
43
|
}
|
|
45
|
-
} catch {
|
|
46
|
-
|
|
44
|
+
} catch (error) {
|
|
45
|
+
(0, _utils.reportError)(new _EngineError.EngineError({
|
|
47
46
|
severity: _const.LogSeverity.WARNING,
|
|
48
47
|
message: `Pub/Sub message is not valid JSON ${subscription.name}`,
|
|
49
48
|
data: {
|
|
50
49
|
subscription,
|
|
51
50
|
message: message.data.toString()
|
|
52
51
|
}
|
|
53
|
-
});
|
|
52
|
+
}));
|
|
53
|
+
// Acknowledge invalid messages to prevent redelivery
|
|
54
|
+
message.ack();
|
|
55
|
+
return;
|
|
54
56
|
}
|
|
57
|
+
|
|
58
|
+
// Process all handlers
|
|
59
|
+
let hasError = false;
|
|
55
60
|
for (const handler of handlers) {
|
|
56
61
|
try {
|
|
57
62
|
await handler(payload, message.attributes, message.publishTime);
|
|
58
63
|
} catch (error) {
|
|
64
|
+
hasError = true;
|
|
59
65
|
(0, _utils.reportError)(error);
|
|
60
66
|
}
|
|
61
67
|
}
|
|
68
|
+
|
|
69
|
+
// Acknowledge message only after successful processing
|
|
70
|
+
// If there was an error, nack the message so it can be redelivered
|
|
71
|
+
if (hasError) {
|
|
72
|
+
(0, _utils.reportDebug)({
|
|
73
|
+
namespace,
|
|
74
|
+
message: `Nacking message due to handler errors [${subscription.name}]`
|
|
75
|
+
});
|
|
76
|
+
message.nack();
|
|
77
|
+
} else {
|
|
78
|
+
message.ack();
|
|
79
|
+
}
|
|
62
80
|
}
|
|
63
81
|
//# sourceMappingURL=handleMessage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleMessage.js","names":["_const","require","_EngineError","_utils","namespace","handleMessage","message","subscription","handlers","isDebezium","reportDebug","name","
|
|
1
|
+
{"version":3,"file":"handleMessage.js","names":["_const","require","_EngineError","_utils","namespace","handleMessage","message","subscription","handlers","isDebezium","reportDebug","name","payload","JSON","parse","data","toString","before","after","diff","Object","keys","filter","key","EngineError","severity","LogSeverity","WARNING","error","reportError","ack","hasError","handler","attributes","publishTime","nack"],"sources":["../../../../src/entities/PubSub/handleMessage/handleMessage.ts"],"sourcesContent":["import { Subscription, Message } from '@google-cloud/pubsub';\nimport { PubSubMessageHandler, PubSubDebeziumPayload } from '../PubSub.types';\nimport { LogSeverity } from 'const';\nimport { EngineError } from 'entities/EngineError';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PubSub:handleMessage';\n\n/**\n * handle the pubSubClients on message event\n * @param message - Message to parse and handle\n * @param subscription - The related Pub/Sub subscription object\n * @param handlers - Message handlers\n * @param isDebezium - Indicates that the subscription from Debezium events\n */\nexport async function handleMessage(\n message: Message,\n subscription: Subscription,\n handlers: Array<PubSubMessageHandler<unknown>>,\n isDebezium = false\n): Promise<void> {\n reportDebug({\n namespace,\n message: `Received message from Pub/Sub [${subscription.name}]`\n });\n\n let payload;\n try {\n payload = JSON.parse(message.data.toString()) as unknown;\n\n // When handling Debezium message, we calculate automatically the keys that have changed between the previous and current state\n if (isDebezium) {\n payload = payload as PubSubDebeziumPayload<Record<string, unknown>>;\n const { before, after } = payload;\n if (before && after)\n payload.diff = Object.keys(after).filter(\n (key) => after[key] !== before[key]\n );\n else if (before) payload.diff = Object.keys(before);\n else if (after) payload.diff = Object.keys(after);\n else\n throw new EngineError({\n severity: LogSeverity.WARNING,\n message: `Debezium Pub/Sub message did not include before nor after value [${subscription.name}]`,\n data: { subscription, message: message.data.toString() }\n });\n }\n } catch (error) {\n reportError(\n new EngineError({\n severity: LogSeverity.WARNING,\n message: `Pub/Sub message is not valid JSON ${subscription.name}`,\n data: { subscription, message: message.data.toString() }\n })\n );\n // Acknowledge invalid messages to prevent redelivery\n message.ack();\n return;\n }\n\n // Process all handlers\n let hasError = false;\n for (const handler of handlers) {\n try {\n await handler(payload, message.attributes, message.publishTime);\n } catch (error) {\n hasError = true;\n reportError(error);\n }\n }\n\n // Acknowledge message only after successful processing\n // If there was an error, nack the message so it can be redelivered\n if (hasError) {\n reportDebug({\n namespace,\n message: `Nacking message due to handler errors [${subscription.name}]`\n });\n message.nack();\n } else {\n message.ack();\n }\n}\n"],"mappings":";;;;;;AAEA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAEA,MAAMG,SAAS,GAAG,6BAA6B;;AAE/C;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAeC,aAAaA,CACjCC,OAAgB,EAChBC,YAA0B,EAC1BC,QAA8C,EAC9CC,UAAU,GAAG,KAAK,EACH;EACf,IAAAC,kBAAW,EAAC;IACVN,SAAS;IACTE,OAAO,EAAE,kCAAkCC,YAAY,CAACI,IAAI;EAC9D,CAAC,CAAC;EAEF,IAAIC,OAAO;EACX,IAAI;IACFA,OAAO,GAAGC,IAAI,CAACC,KAAK,CAACR,OAAO,CAACS,IAAI,CAACC,QAAQ,CAAC,CAAC,CAAY;;IAExD;IACA,IAAIP,UAAU,EAAE;MACdG,OAAO,GAAGA,OAAyD;MACnE,MAAM;QAAEK,MAAM;QAAEC;MAAM,CAAC,GAAGN,OAAO;MACjC,IAAIK,MAAM,IAAIC,KAAK,EACjBN,OAAO,CAACO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACH,KAAK,CAAC,CAACI,MAAM,CACrCC,GAAG,IAAKL,KAAK,CAACK,GAAG,CAAC,KAAKN,MAAM,CAACM,GAAG,CACpC,CAAC,CAAC,KACC,IAAIN,MAAM,EAAEL,OAAO,CAACO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACJ,MAAM,CAAC,CAAC,KAC/C,IAAIC,KAAK,EAAEN,OAAO,CAACO,IAAI,GAAGC,MAAM,CAACC,IAAI,CAACH,KAAK,CAAC,CAAC,KAEhD,MAAM,IAAIM,wBAAW,CAAC;QACpBC,QAAQ,EAAEC,kBAAW,CAACC,OAAO;QAC7BrB,OAAO,EAAE,oEAAoEC,YAAY,CAACI,IAAI,GAAG;QACjGI,IAAI,EAAE;UAAER,YAAY;UAAED,OAAO,EAAEA,OAAO,CAACS,IAAI,CAACC,QAAQ,CAAC;QAAE;MACzD,CAAC,CAAC;IACN;EACF,CAAC,CAAC,OAAOY,KAAK,EAAE;IACd,IAAAC,kBAAW,EACT,IAAIL,wBAAW,CAAC;MACdC,QAAQ,EAAEC,kBAAW,CAACC,OAAO;MAC7BrB,OAAO,EAAE,qCAAqCC,YAAY,CAACI,IAAI,EAAE;MACjEI,IAAI,EAAE;QAAER,YAAY;QAAED,OAAO,EAAEA,OAAO,CAACS,IAAI,CAACC,QAAQ,CAAC;MAAE;IACzD,CAAC,CACH,CAAC;IACD;IACAV,OAAO,CAACwB,GAAG,CAAC,CAAC;IACb;EACF;;EAEA;EACA,IAAIC,QAAQ,GAAG,KAAK;EACpB,KAAK,MAAMC,OAAO,IAAIxB,QAAQ,EAAE;IAC9B,IAAI;MACF,MAAMwB,OAAO,CAACpB,OAAO,EAAEN,OAAO,CAAC2B,UAAU,EAAE3B,OAAO,CAAC4B,WAAW,CAAC;IACjE,CAAC,CAAC,OAAON,KAAK,EAAE;MACdG,QAAQ,GAAG,IAAI;MACf,IAAAF,kBAAW,EAACD,KAAK,CAAC;IACpB;EACF;;EAEA;EACA;EACA,IAAIG,QAAQ,EAAE;IACZ,IAAArB,kBAAW,EAAC;MACVN,SAAS;MACTE,OAAO,EAAE,0CAA0CC,YAAY,CAACI,IAAI;IACtE,CAAC,CAAC;IACFL,OAAO,CAAC6B,IAAI,CAAC,CAAC;EAChB,CAAC,MAAM;IACL7B,OAAO,CAACwB,GAAG,CAAC,CAAC;EACf;AACF","ignoreList":[]}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { PushNotificationNotification } from './PushNotification.types';
|
|
2
2
|
export declare const PushNotification: {
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Initialize the push notification entity
|
|
5
|
+
* Registers the publisher with optimal settings for push notifications
|
|
6
|
+
*/
|
|
7
|
+
init(): Promise<void>;
|
|
5
8
|
/**
|
|
6
9
|
* Send a push notification through the push service
|
|
7
10
|
* @param userId - ID of the user that should receive the notification
|
|
8
11
|
* @param notification - Notification that should be sent
|
|
12
|
+
* @throws {EngineError} If the topic is not initialized or publish fails
|
|
9
13
|
*/
|
|
10
14
|
sendPush(userId: string, notification: PushNotificationNotification): Promise<void>;
|
|
11
15
|
};
|
|
@@ -9,8 +9,11 @@ var _PubSub = require("../PubSub");
|
|
|
9
9
|
var _utils = require("../../utils");
|
|
10
10
|
const namespace = 'engine:PushNotification';
|
|
11
11
|
const PushNotification = exports.PushNotification = {
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the push notification entity
|
|
14
|
+
* Registers the publisher with optimal settings for push notifications
|
|
15
|
+
*/
|
|
16
|
+
async init() {
|
|
14
17
|
(0, _utils.reportDebug)({
|
|
15
18
|
namespace,
|
|
16
19
|
message: 'Initializing PushNotification'
|
|
@@ -18,27 +21,60 @@ const PushNotification = exports.PushNotification = {
|
|
|
18
21
|
if (!process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC) throw new _.EngineError({
|
|
19
22
|
message: 'Environment variable PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC is not defined'
|
|
20
23
|
});
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
|
|
25
|
+
// Register publisher with optimized settings for push notifications
|
|
26
|
+
_PubSub.PubSub.addPublisher(process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC, {
|
|
27
|
+
enableMessageOrdering: false,
|
|
28
|
+
// Push notifications don't need strict ordering
|
|
29
|
+
batching: {
|
|
30
|
+
maxMessages: 100,
|
|
31
|
+
maxBytes: 1024 * 1024,
|
|
32
|
+
// 1MB
|
|
33
|
+
maxMilliseconds: 50 // Fast delivery for notifications
|
|
34
|
+
},
|
|
35
|
+
retry: {
|
|
36
|
+
initialDelayMillis: 100,
|
|
37
|
+
maxDelayMillis: 60000,
|
|
38
|
+
delayMultiplier: 1.3
|
|
39
|
+
}
|
|
40
|
+
});
|
|
23
41
|
},
|
|
24
42
|
/**
|
|
25
43
|
* Send a push notification through the push service
|
|
26
44
|
* @param userId - ID of the user that should receive the notification
|
|
27
45
|
* @param notification - Notification that should be sent
|
|
46
|
+
* @throws {EngineError} If the topic is not initialized or publish fails
|
|
28
47
|
*/
|
|
29
48
|
async sendPush(userId, notification) {
|
|
49
|
+
if (!userId) {
|
|
50
|
+
throw new _.EngineError({
|
|
51
|
+
message: 'userId is required to send push notification'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
30
54
|
(0, _utils.reportDebug)({
|
|
31
55
|
namespace,
|
|
32
|
-
message: `
|
|
56
|
+
message: `Sending push notification to user '${userId}'`,
|
|
33
57
|
data: {
|
|
34
58
|
userId,
|
|
35
59
|
notification
|
|
36
60
|
}
|
|
37
61
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
try {
|
|
63
|
+
await _PubSub.PubSub.publish(process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC, {
|
|
64
|
+
...notification,
|
|
65
|
+
userId
|
|
66
|
+
});
|
|
67
|
+
} catch (error) {
|
|
68
|
+
(0, _utils.reportError)(new _.EngineError({
|
|
69
|
+
message: `Failed to send push notification to user '${userId}'`,
|
|
70
|
+
data: {
|
|
71
|
+
userId,
|
|
72
|
+
notification,
|
|
73
|
+
error
|
|
74
|
+
}
|
|
75
|
+
}));
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
42
78
|
}
|
|
43
79
|
};
|
|
44
80
|
//# sourceMappingURL=PushNotification.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PushNotification.js","names":["_","require","_PubSub","_utils","namespace","PushNotification","exports","init","reportDebug","message","process","env","PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC","EngineError","PubSub","addPublisher","sendPush","userId","notification","data","publish"],"sources":["../../../src/entities/PushNotification/PushNotification.ts"],"sourcesContent":["import { PushNotificationNotification } from './PushNotification.types';\nimport { EngineError } from 'entities';\nimport { PubSub } from 'entities/PubSub';\nimport { reportDebug } from 'utils';\n\nconst namespace = 'engine:PushNotification';\n\nexport const PushNotification = {\n
|
|
1
|
+
{"version":3,"file":"PushNotification.js","names":["_","require","_PubSub","_utils","namespace","PushNotification","exports","init","reportDebug","message","process","env","PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC","EngineError","PubSub","addPublisher","enableMessageOrdering","batching","maxMessages","maxBytes","maxMilliseconds","retry","initialDelayMillis","maxDelayMillis","delayMultiplier","sendPush","userId","notification","data","publish","error","reportError"],"sources":["../../../src/entities/PushNotification/PushNotification.ts"],"sourcesContent":["import { PushNotificationNotification } from './PushNotification.types';\nimport { EngineError } from 'entities';\nimport { PubSub } from 'entities/PubSub';\nimport { reportDebug, reportError } from 'utils';\n\nconst namespace = 'engine:PushNotification';\n\nexport const PushNotification = {\n /**\n * Initialize the push notification entity\n * Registers the publisher with optimal settings for push notifications\n */\n async init(): Promise<void> {\n reportDebug({ namespace, message: 'Initializing PushNotification' });\n if (!process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC)\n throw new EngineError({\n message:\n 'Environment variable PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC is not defined'\n });\n \n // Register publisher with optimized settings for push notifications\n PubSub.addPublisher(process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC, {\n enableMessageOrdering: false, // Push notifications don't need strict ordering\n batching: {\n maxMessages: 100,\n maxBytes: 1024 * 1024, // 1MB\n maxMilliseconds: 50 // Fast delivery for notifications\n },\n retry: {\n initialDelayMillis: 100,\n maxDelayMillis: 60000,\n delayMultiplier: 1.3\n }\n });\n },\n\n /**\n * Send a push notification through the push service\n * @param userId - ID of the user that should receive the notification\n * @param notification - Notification that should be sent\n * @throws {EngineError} If the topic is not initialized or publish fails\n */\n async sendPush(\n userId: string,\n notification: PushNotificationNotification\n ): Promise<void> {\n if (!userId) {\n throw new EngineError({\n message: 'userId is required to send push notification'\n });\n }\n\n reportDebug({\n namespace,\n message: `Sending push notification to user '${userId}'`,\n data: { userId, notification }\n });\n\n try {\n await PubSub.publish(\n process.env.PUBSUB_PUSH_NOTIFICATION_QUEUE_TOPIC as string,\n {\n ...notification,\n userId\n }\n );\n } catch (error) {\n reportError(\n new EngineError({\n message: `Failed to send push notification to user '${userId}'`,\n data: { userId, notification, error }\n })\n );\n throw error;\n }\n }\n};\n"],"mappings":";;;;;;AACA,IAAAA,CAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAEA,MAAMG,SAAS,GAAG,yBAAyB;AAEpC,MAAMC,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG;EAC9B;AACF;AACA;AACA;EACE,MAAME,IAAIA,CAAA,EAAkB;IAC1B,IAAAC,kBAAW,EAAC;MAAEJ,SAAS;MAAEK,OAAO,EAAE;IAAgC,CAAC,CAAC;IACpE,IAAI,CAACC,OAAO,CAACC,GAAG,CAACC,oCAAoC,EACnD,MAAM,IAAIC,aAAW,CAAC;MACpBJ,OAAO,EACL;IACJ,CAAC,CAAC;;IAEJ;IACAK,cAAM,CAACC,YAAY,CAACL,OAAO,CAACC,GAAG,CAACC,oCAAoC,EAAE;MACpEI,qBAAqB,EAAE,KAAK;MAAE;MAC9BC,QAAQ,EAAE;QACRC,WAAW,EAAE,GAAG;QAChBC,QAAQ,EAAE,IAAI,GAAG,IAAI;QAAE;QACvBC,eAAe,EAAE,EAAE,CAAC;MACtB,CAAC;MACDC,KAAK,EAAE;QACLC,kBAAkB,EAAE,GAAG;QACvBC,cAAc,EAAE,KAAK;QACrBC,eAAe,EAAE;MACnB;IACF,CAAC,CAAC;EACJ,CAAC;EAED;AACF;AACA;AACA;AACA;AACA;EACE,MAAMC,QAAQA,CACZC,MAAc,EACdC,YAA0C,EAC3B;IACf,IAAI,CAACD,MAAM,EAAE;MACX,MAAM,IAAIb,aAAW,CAAC;QACpBJ,OAAO,EAAE;MACX,CAAC,CAAC;IACJ;IAEA,IAAAD,kBAAW,EAAC;MACVJ,SAAS;MACTK,OAAO,EAAE,sCAAsCiB,MAAM,GAAG;MACxDE,IAAI,EAAE;QAAEF,MAAM;QAAEC;MAAa;IAC/B,CAAC,CAAC;IAEF,IAAI;MACF,MAAMb,cAAM,CAACe,OAAO,CAClBnB,OAAO,CAACC,GAAG,CAACC,oCAAoC,EAChD;QACE,GAAGe,YAAY;QACfD;MACF,CACF,CAAC;IACH,CAAC,CAAC,OAAOI,KAAK,EAAE;MACd,IAAAC,kBAAW,EACT,IAAIlB,aAAW,CAAC;QACdJ,OAAO,EAAE,6CAA6CiB,MAAM,GAAG;QAC/DE,IAAI,EAAE;UAAEF,MAAM;UAAEC,YAAY;UAAEG;QAAM;MACtC,CAAC,CACH,CAAC;MACD,MAAMA,KAAK;IACb;EACF;AACF,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-server-engine",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.13",
|
|
4
4
|
"description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|