@hahnpro/flow-sdk 7.0.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ import { Logger } from './FlowLogger';
2
+ /**
3
+ * Class representing a context manager for handling properties.
4
+ */
5
+ export declare class ContextManager {
6
+ protected logger: Logger;
7
+ private properties;
8
+ /**
9
+ * Constructor of the ContextManager.
10
+ * @param {Logger} logger - The logger instance for logging messages.
11
+ * @param {Record<string, any>} [properties={}] - Initial properties to set.
12
+ */
13
+ constructor(logger: Logger, properties?: Record<string, any>);
14
+ /**
15
+ * Init or overwrite all properties.
16
+ * @param properties
17
+ */
18
+ overwriteAllProperties(properties?: Record<string, any>): void;
19
+ /**
20
+ * Get a copy of the current properties.
21
+ * @returns {Record<string, any>} A copy of the properties.
22
+ */
23
+ getProperties(): Record<string, any>;
24
+ /**
25
+ * Set a property.
26
+ * A property key starting with "flow." is reserved for the properties set by in UI and so it is not allowed to be set.
27
+ * @param {string} keyOrPath - The key or the path of the property.
28
+ * @param {any} value - The value of the property.
29
+ */
30
+ set(keyOrPath: string, value: any): void;
31
+ /**
32
+ * Get a property value by key.
33
+ * @param {string} keyOrPath - The key or the path of the property.
34
+ * @returns {any} The value of the property.
35
+ */
36
+ get(keyOrPath: string): any;
37
+ replaceAllPlaceholderProperties(properties: any): any;
38
+ }
39
+ export declare function flowInterpolate(value: any, properties: Record<string, any>): any;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContextManager = void 0;
4
+ exports.flowInterpolate = flowInterpolate;
5
+ const tslib_1 = require("tslib");
6
+ const lodash_1 = require("lodash");
7
+ const string_interp_1 = tslib_1.__importDefault(require("string-interp"));
8
+ class ContextManager {
9
+ constructor(logger, properties = {}) {
10
+ this.logger = logger;
11
+ this.properties = properties;
12
+ }
13
+ overwriteAllProperties(properties = {}) {
14
+ this.properties = properties;
15
+ }
16
+ getProperties() {
17
+ return { ...this.properties };
18
+ }
19
+ set(keyOrPath, value) {
20
+ if (keyOrPath.startsWith('flow.')) {
21
+ this.logger.error(`Set property of "${keyOrPath}" is not allowed, because it starts with "flow.", so it is reserved for the properties set by in UI.`);
22
+ }
23
+ else {
24
+ (0, lodash_1.set)(this.properties, keyOrPath, value);
25
+ }
26
+ }
27
+ get(keyOrPath) {
28
+ return (0, lodash_1.get)(this.properties, keyOrPath, undefined);
29
+ }
30
+ replaceAllPlaceholderProperties(properties) {
31
+ return flowInterpolate((0, lodash_1.cloneDeep)(properties), this.properties);
32
+ }
33
+ }
34
+ exports.ContextManager = ContextManager;
35
+ function flowInterpolate(value, properties) {
36
+ if (!properties) {
37
+ return value;
38
+ }
39
+ if ((0, lodash_1.isPlainObject)(value)) {
40
+ for (const key of Object.keys(value)) {
41
+ value[key] = flowInterpolate(value[key], properties);
42
+ }
43
+ return value;
44
+ }
45
+ else if (Array.isArray(value) && value.length > 0) {
46
+ value.forEach(function (v, index) {
47
+ this[index] = flowInterpolate(v, properties);
48
+ }, value);
49
+ return value;
50
+ }
51
+ else if (value != null && typeof value === 'string' && value.startsWith('${')) {
52
+ const blockRegEx = /\$\{\s*(\S+)\s*}/g;
53
+ let newValue = value;
54
+ let m;
55
+ do {
56
+ m = blockRegEx.exec(value);
57
+ if (m?.[1].startsWith('flow.')) {
58
+ newValue = newValue.replace(m[0], interpolate(m[0], { flow: properties.flow }));
59
+ }
60
+ } while (m);
61
+ return newValue;
62
+ }
63
+ else {
64
+ return value;
65
+ }
66
+ }
67
+ function interpolate(text, templateVariables) {
68
+ try {
69
+ return (0, string_interp_1.default)(text, templateVariables) ?? text;
70
+ }
71
+ catch (err) {
72
+ return text;
73
+ }
74
+ }
@@ -9,6 +9,7 @@ import { FlowEvent } from './FlowEvent';
9
9
  import { Logger } from './FlowLogger';
10
10
  import { RpcClient } from './RpcClient';
11
11
  import { NatsConnection, ConnectionOptions as NatsConnectionOptions } from 'nats';
12
+ import { ContextManager } from './ContextManager';
12
13
  interface FlowAppConfig {
13
14
  logger?: Logger;
14
15
  amqpConfig?: AmqpConnectionConfig;
@@ -38,14 +39,16 @@ export declare class FlowApplication {
38
39
  private outputStreamMap;
39
40
  private outputQueueMetrics;
40
41
  private performanceMap;
41
- private properties;
42
42
  private readonly skipApi;
43
43
  private readonly apiClient?;
44
+ private readonly contextManager;
44
45
  constructor(modules: ClassType<any>[], flow: Flow, config?: FlowAppConfig);
45
46
  constructor(modules: ClassType<any>[], flow: Flow, baseLogger?: Logger, amqpConnection?: AmqpConnection, natsConnection?: NatsConnection, skipApi?: boolean, explicitInit?: boolean);
46
47
  get rpcClient(): RpcClient;
47
48
  get api(): API;
48
49
  get natsConnection(): NatsConnection;
50
+ getContextManager(): ContextManager;
51
+ getProperties(): Record<string, any>;
49
52
  init(): Promise<void>;
50
53
  private publishLifecycleEvent;
51
54
  private setQueueMetrics;
@@ -53,7 +56,6 @@ export declare class FlowApplication {
53
56
  subscribe: (streamId: string, observer: PartialObserver<FlowEvent>) => import("rxjs").Subscription;
54
57
  emit: (event: FlowEvent) => void;
55
58
  emitPartial: (completeEvent: FlowEvent, partialEvent: FlowEvent) => void;
56
- getProperties(): Record<string, any>;
57
59
  onMessage: (msg: ConsumeMessage) => Promise<void>;
58
60
  /**
59
61
  * Publish a flow event to the amqp flowlogs exchange.
@@ -10,12 +10,14 @@ const object_sizeof_1 = tslib_1.__importDefault(require("object-sizeof"));
10
10
  const perf_hooks_1 = require("perf_hooks");
11
11
  const rxjs_1 = require("rxjs");
12
12
  const operators_1 = require("rxjs/operators");
13
+ const lodash_1 = require("lodash");
13
14
  const amqp_1 = require("./amqp");
14
15
  const flow_interface_1 = require("./flow.interface");
15
16
  const FlowLogger_1 = require("./FlowLogger");
16
17
  const RpcClient_1 = require("./RpcClient");
17
18
  const utils_1 = require("./utils");
18
19
  const nats_1 = require("./nats");
20
+ const ContextManager_1 = require("./ContextManager");
19
21
  const MAX_EVENT_SIZE_BYTES = +process.env.MAX_EVENT_SIZE_BYTES || 512 * 1024;
20
22
  const WARN_EVENT_PROCESSING_SEC = +process.env.WARN_EVENT_PROCESSING_SEC || 60;
21
23
  const WARN_EVENT_QUEUE_SIZE = +process.env.WARN_EVENT_QUEUE_SIZE || 100;
@@ -125,8 +127,9 @@ class FlowApplication {
125
127
  context = this.context;
126
128
  }
127
129
  if (flow.properties) {
128
- this.properties = flow.properties;
130
+ this.contextManager.overwriteAllProperties({ ...this.contextManager.getProperties(), ...flow.properties });
129
131
  for (const element of Object.values(this.elements)) {
132
+ element.replacePlaceholderAndSetProperties();
130
133
  element.onFlowPropertiesChanged?.(flow.properties);
131
134
  }
132
135
  }
@@ -137,7 +140,8 @@ class FlowApplication {
137
140
  }
138
141
  }
139
142
  for (const element of flow.elements || []) {
140
- this.elements?.[element.id]?.onPropertiesChanged(element.properties);
143
+ this.elements?.[element.id]?.setPropertiesWithPlaceholders((0, lodash_1.cloneDeep)(element.properties));
144
+ this.elements?.[element.id]?.onPropertiesChanged(this.contextManager.replaceAllPlaceholderProperties(this.elements[element.id].getPropertiesWithPlaceholders()));
141
145
  }
142
146
  const statusEvent = {
143
147
  eventId: (0, crypto_1.randomUUID)(),
@@ -224,6 +228,7 @@ class FlowApplication {
224
228
  this._api = mockApi || null;
225
229
  }
226
230
  this.logger = new FlowLogger_1.FlowLogger({ id: 'none', functionFqn: 'FlowApplication', ...flow?.context }, this.baseLogger || undefined, this.publishEvent);
231
+ this.contextManager = new ContextManager_1.ContextManager(this.logger, this.flow?.properties);
227
232
  process.once('uncaughtException', (err) => {
228
233
  this.logger.error('Uncaught exception!');
229
234
  this.logger.error(err);
@@ -253,14 +258,25 @@ class FlowApplication {
253
258
  get natsConnection() {
254
259
  return this._natsConnection;
255
260
  }
261
+ getContextManager() {
262
+ return this.contextManager;
263
+ }
264
+ getProperties() {
265
+ return this.contextManager.getProperties();
266
+ }
256
267
  async init() {
257
268
  if (this.initialized)
258
269
  return;
259
270
  this.context = { ...this.flow.context };
260
- this.properties = this.flow.properties || {};
271
+ this.contextManager.overwriteAllProperties(this.flow.properties ?? {});
261
272
  try {
262
273
  if (!this.skipApi && !(this._api instanceof hpc_api_1.MockAPI)) {
263
- this._api = new hpc_api_1.API(this.apiClient);
274
+ let tokenSubject;
275
+ const { owner, runAsOwner } = this.context;
276
+ if (runAsOwner && owner) {
277
+ tokenSubject = owner.type === 'org' ? 'org-admin-' + owner.id : owner.id;
278
+ }
279
+ this._api = new hpc_api_1.API(this.apiClient, { tokenSubject });
264
280
  }
265
281
  }
266
282
  catch (err) {
@@ -322,7 +338,8 @@ class FlowApplication {
322
338
  const { id, name, properties, module, functionFqn } = element;
323
339
  try {
324
340
  const context = { ...this.context, id, name, logger: this.baseLogger, app: this };
325
- this.elements[id] = new this.declarations[`${module}.${functionFqn}`](context, properties);
341
+ this.elements[id] = new this.declarations[`${module}.${functionFqn}`](context, this.contextManager.replaceAllPlaceholderProperties(properties));
342
+ this.elements[id].setPropertiesWithPlaceholders((0, lodash_1.cloneDeep)(properties));
326
343
  }
327
344
  catch (err) {
328
345
  await logErrorAndExit(`Could not create FlowElement for ${module}.${functionFqn}`);
@@ -387,9 +404,6 @@ class FlowApplication {
387
404
  this.initialized = true;
388
405
  this.logger.log('Flow Deployment is running');
389
406
  }
390
- getProperties() {
391
- return this.properties;
392
- }
393
407
  async destroy(exitCode = 0) {
394
408
  try {
395
409
  try {
@@ -12,14 +12,28 @@ export declare abstract class FlowElement<T = any> {
12
12
  protected readonly logger: FlowLogger;
13
13
  protected metadata: FlowElementContext;
14
14
  protected properties: T;
15
+ private propertiesWithPlaceholders;
15
16
  private readonly app?;
16
17
  private readonly rpcRoutingKey;
17
18
  private stopPropagateStream;
18
19
  constructor({ app, logger, ...metadata }: Context, properties?: unknown, propertiesClassType?: ClassType<T>, whitelist?: boolean);
20
+ /**
21
+ * Sets the placeholder properties for this flow element
22
+ * @param propertiesWithPlaceholders
23
+ */
24
+ setPropertiesWithPlaceholders(propertiesWithPlaceholders: T): void;
25
+ /**
26
+ * Returns the placeholder properties for this flow element
27
+ */
28
+ getPropertiesWithPlaceholders(): T;
19
29
  get flowProperties(): Record<string, any>;
20
30
  get natsConnection(): import("nats").NatsConnection;
21
31
  onDestroy?: () => void;
22
32
  onMessage?: (message: DeploymentMessage) => void;
33
+ /**
34
+ * Replace all placeholder properties with their explicit updated value and set them as the properties of the element
35
+ */
36
+ replacePlaceholderAndSetProperties(): void;
23
37
  onFlowPropertiesChanged?: (properties: Record<string, any>) => void;
24
38
  onContextChanged: (context: Partial<FlowContext>) => void;
25
39
  onPropertiesChanged: (properties: T) => void;
@@ -30,7 +30,7 @@ class FlowElement {
30
30
  }
31
31
  };
32
32
  this.handleApiError = (error) => (0, utils_1.handleApiError)(error, this.logger);
33
- this.interpolate = (value, ...templateVariables) => (0, utils_1.fillTemplate)(value, ...templateVariables);
33
+ this.interpolate = (value, ...templateVariables) => (0, utils_1.fillTemplate)(value, ...templateVariables, { flow: this.app?.getContextManager().getProperties().flow ?? {} });
34
34
  this.app = app;
35
35
  this.api = this.app?.api;
36
36
  this.metadata = { ...metadata, functionFqn: this.functionFqn };
@@ -40,12 +40,24 @@ class FlowElement {
40
40
  this.setProperties(properties);
41
41
  }
42
42
  }
43
+ setPropertiesWithPlaceholders(propertiesWithPlaceholders) {
44
+ this.propertiesWithPlaceholders = propertiesWithPlaceholders;
45
+ }
46
+ getPropertiesWithPlaceholders() {
47
+ return this.propertiesWithPlaceholders;
48
+ }
43
49
  get flowProperties() {
44
50
  return this.app?.getProperties?.() || {};
45
51
  }
46
52
  get natsConnection() {
47
53
  return this.app?.natsConnection;
48
54
  }
55
+ replacePlaceholderAndSetProperties() {
56
+ const placeholderProperties = this.propertiesWithPlaceholders;
57
+ if (this.propertiesWithPlaceholders) {
58
+ this.setProperties(this.app.getContextManager()?.replaceAllPlaceholderProperties(placeholderProperties));
59
+ }
60
+ }
49
61
  emitOutput(data = {}, outputId = 'default', time = new Date()) {
50
62
  return this.emitEvent(data, null, outputId, time);
51
63
  }
@@ -1,7 +1,10 @@
1
+ import { Owner } from '@hahnpro/hpc-api';
1
2
  export interface FlowContext {
2
3
  deploymentId?: string;
3
4
  diagramId?: string;
4
5
  flowId?: string;
6
+ owner?: Owner;
7
+ runAsOwner?: boolean;
5
8
  }
6
9
  export interface FlowElementContext extends FlowContext {
7
10
  id: string;
package/dist/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export * from './FlowLogger';
8
8
  export * from './FlowModule';
9
9
  export * from './TestModule';
10
10
  export * from './unit-decorators';
11
+ export * from './ContextManager';
11
12
  export { IncompatableWith } from './extra-validators';
package/dist/index.js CHANGED
@@ -12,5 +12,6 @@ tslib_1.__exportStar(require("./FlowLogger"), exports);
12
12
  tslib_1.__exportStar(require("./FlowModule"), exports);
13
13
  tslib_1.__exportStar(require("./TestModule"), exports);
14
14
  tslib_1.__exportStar(require("./unit-decorators"), exports);
15
+ tslib_1.__exportStar(require("./ContextManager"), exports);
15
16
  var extra_validators_1 = require("./extra-validators");
16
17
  Object.defineProperty(exports, "IncompatableWith", { enumerable: true, get: function () { return extra_validators_1.IncompatableWith; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hahnpro/flow-sdk",
3
- "version": "7.0.2",
3
+ "version": "8.0.0",
4
4
  "description": "SDK for building Flow Modules",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -36,19 +36,19 @@
36
36
  "reflect-metadata": "0.2.2",
37
37
  "rxjs": "7.8.1",
38
38
  "string-interp": "0.3.6",
39
- "@hahnpro/hpc-api": "5.2.8"
39
+ "@hahnpro/hpc-api": "5.3.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/amqplib": "0.10.5",
43
43
  "@types/jest": "29.5.12",
44
44
  "@types/lodash": "4.17.7",
45
- "@types/node": "20.14.15",
45
+ "@types/node": "20.16.5",
46
46
  "class-validator-jsonschema": "5.0.1",
47
47
  "jest": "29.7.0",
48
- "typescript": "5.5.4"
48
+ "typescript": "5.6.2"
49
49
  },
50
50
  "peerDependencies": {
51
- "axios": "1.7.4",
51
+ "axios": "1.7.7",
52
52
  "class-transformer": "0.5.1",
53
53
  "class-validator": "0.14.1",
54
54
  "lodash": "4.17.21",