corebasic 1.0.240 → 1.0.242

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/dist/libs/dip.js CHANGED
@@ -60,7 +60,7 @@ export const IdempotentDip = () => {
60
60
  };
61
61
  return idip;
62
62
  };
63
- export const idip = async (message, callback, cleanup) => {
63
+ export const idip = async (meta, txn, callback, cleanup) => {
64
64
  if (!callback)
65
65
  throw new Error("Dip.idip() callback argument undefined");
66
66
  if (!cleanup)
@@ -73,9 +73,9 @@ export const idip = async (message, callback, cleanup) => {
73
73
  else
74
74
  await cleanup(IDip);
75
75
  }
76
- let IDip = await IdempotentDip().new(message.meta, message.txn);
76
+ let IDip = await IdempotentDip().new(meta, txn);
77
77
  if (!IDip) {
78
- IDip = await IdempotentDip().new(message.meta, message.txn, { blank: true });
78
+ IDip = await IdempotentDip().new(meta, txn, { blank: true });
79
79
  await doCleanup(IDip);
80
80
  return;
81
81
  }
@@ -87,7 +87,7 @@ export const insertKeys = (message, meta) => {
87
87
  if (!isDipMeta(meta))
88
88
  throw new Error('Error: Provided meta is not an instance of DipMeta in Dip.insertKeys()');
89
89
  return {
90
- "created": message.date, "updated": message.date,
90
+ "created": message.meta.date, "updated": message.meta.date,
91
91
  "createdBy": message.meta.staff,
92
92
  "updatedBy": message.meta.staff,
93
93
  "createdByUser": message.meta.user,
@@ -98,7 +98,7 @@ export const insertKeys = (message, meta) => {
98
98
  };
99
99
  export const updateKeys = (message) => {
100
100
  return {
101
- "updated": message.date,
101
+ "updated": message.meta.date,
102
102
  "updatedBy": message.meta.staff,
103
103
  "updatedByUser": message.meta.user,
104
104
  };
@@ -93,16 +93,17 @@ export const send = async (meta, feature, data, params) => {
93
93
  writableEnded: false,
94
94
  };
95
95
  await apiHandler(req, res);
96
- return response; // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.
96
+ return response.data; // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.
97
97
  }
98
98
  // Workaround: https://stackoverflow.com/questions/72343387/axios-not-sending-headers-request-failing-getting-401-error
99
- return (await axios({
99
+ const response = (await axios({
100
100
  method,
101
101
  url: `${service}${url}`,
102
102
  headers: { jwt: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN },
103
103
  data: payload,
104
104
  timeout: 30000 // 30 secs
105
- })).data; // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.;
105
+ })).data;
106
+ return response.data; // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.;
106
107
  };
107
108
  let appids = {};
108
109
  async function announce() {
@@ -146,12 +147,11 @@ const apiHandler = async (req, res) => {
146
147
  throw { status: 404, message: `Resource not found. Feature ${req.body.feature} not available.` };
147
148
  }
148
149
  feature = feature; // typescript validation with !
149
- let topic = feature.topic;
150
150
  let params = getFeatureUrl(feature.api).split("/").filter((item) => item.startsWith(":")).map((item) => item.replace(":", ""));
151
151
  for (let param of params)
152
152
  if (!req.params[param])
153
153
  throw { status: 404, message: "Resource not found. One or more url parameter not specified." };
154
- let meta = { ...req.body, data: undefined }; // TODO: Must also include invoiceTxn
154
+ let meta = { ...req.body, data: undefined, topic: feature.topic, date: new Date().getTime() }; // TODO: Must also include invoiceTxn
155
155
  if (process.env.USE_DEFAULT_COMPANY) {
156
156
  meta.company = 'DEFAULT_COMPANY';
157
157
  meta.outlet = 'DEFAULT_OUTLET';
@@ -161,13 +161,6 @@ const apiHandler = async (req, res) => {
161
161
  meta,
162
162
  data: req.body.data,
163
163
  params: req.params,
164
- // To be removed 1
165
- date: new Date().getTime(),
166
- // To be removed 2
167
- topic,
168
- feature: req.body.feature,
169
- user: req.body.user,
170
- txn: meta.txn,
171
164
  };
172
165
  const appReq = {
173
166
  meta: message.meta,
@@ -183,34 +176,36 @@ const apiHandler = async (req, res) => {
183
176
  const appRes = res;
184
177
  if (method === "get") {
185
178
  try {
186
- // await feature.handler({...req, headers: req.headers, body: {...req.body, topic} }, res)
187
- // req.body = {...req.body, topic}
188
- await feature.handler(appReq, appRes);
179
+ const data = await feature.handler(message, appReq, appRes);
180
+ if (!appRes.headersSent)
181
+ res.json({ data });
189
182
  }
190
183
  catch (err) {
191
184
  if (process.env.DEBUG_MODE)
192
- console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
185
+ console.error('Error: Feature: ', message.meta.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
193
186
  throw { status: 500, message: "Failed to GET feature", ...err };
194
187
  }
195
188
  }
196
189
  else if (method !== "get" && feature.bypass) {
197
190
  try {
198
- await feature.handler(topic, message, appReq, appRes);
191
+ const data = await feature.handler(message, appReq, appRes);
192
+ if (!appRes.headersSent)
193
+ res.json({ data, event: message.meta.feature, txn: message.meta.txn, bypass: true });
199
194
  }
200
195
  catch (err) {
201
196
  if (process.env.DEBUG_MODE)
202
- console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
197
+ console.error('Error: Feature: ', message.meta.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err);
203
198
  throw { status: 500, message: "Failed to POST feature", ...err };
204
199
  }
205
200
  }
206
201
  else {
207
202
  try {
208
- await Kafka.send(`Features.${topic}`, message, message.user, { compression: Kafka.CompressionTypes.GZIP });
203
+ await Kafka.send(`Features.${message.meta.topic}`, message, message.meta.user, { compression: Kafka.CompressionTypes.GZIP });
209
204
  }
210
205
  catch {
211
206
  throw { status: 500, message: "Failed to queue the transaction" };
212
207
  }
213
- appRes.json({ data: { txn: message.txn, success: true, status: "Queued", featureQueued: true } });
208
+ appRes.json({ data: { txn: message.meta.txn, success: true, status: "Queued", featureQueued: true } });
214
209
  }
215
210
  }
216
211
  catch (err) {
@@ -243,9 +238,9 @@ async function registerHandler(features) {
243
238
  if (featurePath === 'src/transactions/query')
244
239
  continue;
245
240
  if (featureName.startsWith("flows.") && !featureName.startsWith("flows.query.") && !featureName.startsWith("flows.command.") && !featureName.startsWith("flows.designs."))
246
- feature.handler = (await import(__rewriteRelativeImportExtension(`${new URL(`src/flows/query.js`, PROJECT_ROOT_URL).toString().replace('file://', '')}`))).FEATURE_HANDLERS[handler];
241
+ feature.handler = (await import(__rewriteRelativeImportExtension(`${new URL(`src/flows/query.ts`, PROJECT_ROOT_URL).toString().replace('file://', '')}`))).FEATURE_HANDLERS[handler];
247
242
  else
248
- feature.handler = (await import(__rewriteRelativeImportExtension(`${new URL(`${featurePath}.js`, PROJECT_ROOT_URL).toString().replace('file://', '')}`)))[handler];
243
+ feature.handler = (await import(__rewriteRelativeImportExtension(`${new URL(`${featurePath}.ts`, PROJECT_ROOT_URL).toString().replace('file://', '')}`)))[handler];
249
244
  }
250
245
  }
251
246
  function registerApi() {
@@ -291,31 +286,31 @@ export const start = async (app, url, file) => {
291
286
  for (let topic of kafkaTopics) {
292
287
  const groupId = '';
293
288
  Kafka.receive(`Features.${topic}`, groupId, async (topic, message) => {
289
+ topic = topic.replace(/^.*Features./, '');
294
290
  if (Utils.isEmpty(message.meta?.company))
295
291
  message.meta = { ...message.meta, company: "GLOBAL", outlet: "GLOBAL" };
296
- if (!message?.feature)
292
+ if (!message.meta.feature)
297
293
  return;
298
294
  const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
299
- topic = topic.replace(/^.*Features./, '');
300
295
  while (true) {
301
296
  try {
302
- // const iso_date = new Date(message.date).toISOString()
297
+ // const iso_date = new Date(message.meta.date).toISOString()
303
298
  // // TODO: use $useChunks: [] once support is added in dip insert
304
- // await Dip.insert(globalMeta, `users.txns`, { _id: `${message.user}_${iso_date}_${message.txn}`, user: message.user, feature: message.feature, date: message.date, created: message.date, updated: message.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
299
+ // await Dip.insert(globalMeta, `users.txns`, { _id: `${message.meta.user}_${iso_date}_${message.meta.txn}`, user: message.meta.user, feature: message.meta.feature, date: message.meta.date, created: message.meta.date, updated: message.meta.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
305
300
  // TODO: Avoid duplicate processing
306
- const feature = features[message.feature];
301
+ const feature = features[message.meta.feature];
307
302
  if (!feature) {
308
- console.warn(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
309
- throw new Error(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`);
303
+ console.warn(`Feature ${message.meta.feature} missing, in executing handler during Kafka.receive(topic: ${message.meta.topic})`);
304
+ throw new Error(`Feature ${message.meta.feature} missing, in executing handler during Kafka.receive(topic: ${message.meta.topic})`);
310
305
  }
311
- await feature.handler(topic, message);
312
- await Dip.insert(globalMeta, "Features.txns", { _id: message.txn, status: "Processed" }, { idempotent: true });
306
+ await feature.handler(message);
307
+ await Dip.insert(globalMeta, "Features.txns", { _id: message.meta.txn, status: "Processed" }, { idempotent: true });
313
308
  break;
314
309
  }
315
310
  catch (err) {
316
311
  if (process.env.DEBUG_MODE)
317
- console.log('Error: Feature: ', message.feature, err);
318
- console.warn(`Feature: ${message.feature}, error in executing handler during Kafka.receive(topic: ${topic})`);
312
+ console.log('Error: Feature: ', message.meta.feature, err);
313
+ console.warn(`Feature: ${message.meta.feature}, error in executing handler during Kafka.receive(topic: ${message.meta.topic})`);
319
314
  }
320
315
  await timer(10000);
321
316
  }
package/libs/dip.ts CHANGED
@@ -90,10 +90,10 @@ export const IdempotentDip = (): IdempotentDip => {
90
90
  return idip
91
91
  }
92
92
 
93
- type DipMessage = { meta: {staff: string; user: string}; txn: string; date: number }
93
+ type DipMessage = { meta: {staff: string; user: string; date: number } }
94
94
  type IdipCleanup = Record<string, string[]> | ((idip: IdempotentDip) => Promise<void>)
95
95
 
96
- export const idip = async (message: {meta: Elabase.DipMeta, txn: string}, callback: (idip: IdempotentDip) => Promise<void>, cleanup: IdipCleanup) => {
96
+ export const idip = async (meta: Elabase.DipMeta, txn: string, callback: (idip: IdempotentDip) => Promise<void>, cleanup: IdipCleanup) => {
97
97
  if (!callback)
98
98
  throw new Error("Dip.idip() callback argument undefined")
99
99
  if (!cleanup)
@@ -106,9 +106,9 @@ export const idip = async (message: {meta: Elabase.DipMeta, txn: string}, callba
106
106
  await cleanup(IDip)
107
107
  }
108
108
 
109
- let IDip = await IdempotentDip().new(message.meta, message.txn)
109
+ let IDip = await IdempotentDip().new(meta, txn)
110
110
  if (!IDip) {
111
- IDip = await IdempotentDip().new(message.meta, message.txn, {blank: true})
111
+ IDip = await IdempotentDip().new(meta, txn, {blank: true})
112
112
  await doCleanup(IDip as IdempotentDip)
113
113
  return
114
114
  }
@@ -121,7 +121,7 @@ export const idip = async (message: {meta: Elabase.DipMeta, txn: string}, callba
121
121
  export const insertKeys = (message: DipMessage, meta: Elabase.DipMeta) => {
122
122
  if (!isDipMeta(meta)) throw new Error('Error: Provided meta is not an instance of DipMeta in Dip.insertKeys()')
123
123
  return {
124
- "created": message.date, "updated": message.date,
124
+ "created": message.meta.date, "updated": message.meta.date,
125
125
  "createdBy": message.meta.staff,
126
126
  "updatedBy": message.meta.staff,
127
127
  "createdByUser": message.meta.user,
@@ -132,7 +132,7 @@ export const insertKeys = (message: DipMessage, meta: Elabase.DipMeta) => {
132
132
  }
133
133
  export const updateKeys = (message: DipMessage) => {
134
134
  return {
135
- "updated": message.date,
135
+ "updated": message.meta.date,
136
136
  "updatedBy": message.meta.staff,
137
137
  "updatedByUser": message.meta.user,
138
138
  }
package/libs/features.ts CHANGED
@@ -25,21 +25,16 @@ type FeatureMeta = {
25
25
  version: string,
26
26
  txn: string,
27
27
  // invoiceTxn?: string // TODO: Must also include invoiceTxn
28
+ topic: string,
29
+ date: number,
28
30
  }
29
31
  export type FeatureParams = Record<string, string>
30
- type FeatureMessage = {
32
+ type FeatureMessage<I = unknown> = {
31
33
  meta: FeatureMeta
32
- data: unknown
34
+ data: I
33
35
  params: FeatureParams
34
- // To be removed 1
35
- date: number // used as message.date
36
- // To be removed 2
37
- topic: string // used as req.body.topic
38
- feature: string // used as req.body.feature or message.feature
39
- user: string // used as req.body.user or message.user
40
- txn: string // used as message.txn
41
-
42
36
 
37
+ // feature // Only used as req.body.feature in features.ts, session.ts
43
38
  // client // Only used as req.body.client in auth.ts, session.ts and messaging.ts
44
39
  // version // Only used as req.body.version in auth.ts and session.ts
45
40
  // outlet, company // Only used as req.body.outlet, req.body.company in session.ts
@@ -90,9 +85,19 @@ type ExpressApplication = { [Method in HttpMethod]: (path: string, handler: Expr
90
85
 
91
86
  // export type Req = unknown
92
87
  // export type Res = unknown
93
- export type Query = (req: Req, res: Res) => Promise<void>
94
- export type Command = (topic: string, message: FeatureMessage, req?: Req, res?: Res) => Promise<void>
95
- type Handler = Query | Command
88
+ // export type Query = (req: Req, res: Res) => Promise<void>
89
+ // export type Command = (message: FeatureMessage, req?: Req, res?: Res) => Promise<void>
90
+ // type Handler = Query | Command
91
+
92
+ declare global {
93
+ type Handler<I, O> = (input: FeatureMessage<I>, req?: Req, res?: Res) => Promise<O>
94
+
95
+ interface FeatureTypes {
96
+ // "privileges.query.check": Handler<{feature: string}, {data: {granted: boolean} }>
97
+ // "products.query.get": typeof import("./src/products/query.ts").get;
98
+ // "products.brands.query.get": typeof import("./src/products/brands/query.ts").get;
99
+ }
100
+ }
96
101
 
97
102
 
98
103
  type RemoteFeatureEntry = {
@@ -103,7 +108,7 @@ type RemoteFeatureEntry = {
103
108
  headers?: {JWT?: string, service?: boolean, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN?: string}
104
109
  }
105
110
 
106
- type FeatureEntry = { api: string, topic: string, bypass: boolean, featureless: boolean, subscribe: string, handler: Handler }
111
+ type FeatureEntry = { api: string, topic: string, bypass: boolean, featureless: boolean, subscribe: string, handler: Handler<unknown, unknown> }
107
112
 
108
113
 
109
114
  let features: Record<string, FeatureEntry> = {}
@@ -151,15 +156,7 @@ export const get = (feature: string) => {
151
156
  return {api, service, topic}
152
157
  }
153
158
 
154
- declare global {
155
- type Handler<I, O> = (input: I) => Promise<O>
156
159
 
157
- interface FeatureTypes {
158
- // "privileges.query.check": Handler<{feature: string}, {data: {granted: boolean} }>
159
- // "products.query.get": typeof import("./src/products/query.ts").get;
160
- // "products.brands.query.get": typeof import("./src/products/brands/query.ts").get;
161
- }
162
- }
163
160
 
164
161
  export const send = async <K extends keyof FeatureTypes> (meta: Partial<FeatureMeta>, feature: K, data?: Parameters<FeatureTypes[K]>[0], params?: FeatureParams): Promise<Awaited<ReturnType<FeatureTypes[K]>>> => {
165
162
  const throwError = () => {throw new Error(`Feature ${feature} not found in internal or external list during inter feature call`)}
@@ -199,16 +196,17 @@ export const send = async <K extends keyof FeatureTypes> (meta: Partial<FeatureM
199
196
  writableEnded: false,
200
197
  }
201
198
  await apiHandler(req, res);
202
- return response as Awaited<ReturnType<FeatureTypes[K]>> // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.
199
+ return (response as unknown as {data: unknown}).data as Awaited<ReturnType<FeatureTypes[K]>> // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.
203
200
  }
204
201
  // Workaround: https://stackoverflow.com/questions/72343387/axios-not-sending-headers-request-failing-getting-401-error
205
- return (await axios({
202
+ const response = (await axios({
206
203
  method,
207
204
  url: `${service}${url}`,
208
205
  headers: { jwt: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN },
209
206
  data: payload,
210
207
  timeout: 30000 // 30 secs
211
- })).data as Awaited<ReturnType<FeatureTypes[K]>> // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.;
208
+ })).data;
209
+ return (response as {data: unknown}).data as Awaited<ReturnType<FeatureTypes[K]>> // tsc already checked that the feature returned proper types at the handler site. Cast here due to ts limitations. So no guarantees lost.;
212
210
  }
213
211
 
214
212
  let appids: Record<string, boolean> = {}
@@ -261,13 +259,12 @@ const apiHandler = async (req: ExpressRequest, res: ExpressResponse) => {
261
259
 
262
260
  feature = feature! // typescript validation with !
263
261
 
264
- let topic = feature.topic
265
262
  let params = getFeatureUrl(feature.api).split("/").filter((item: string) => item.startsWith(":")).map((item: string) => item.replace(":", ""))
266
263
  for (let param of params)
267
264
  if (!req.params[param])
268
265
  throw { status: 404, message: "Resource not found. One or more url parameter not specified." }
269
266
 
270
- let meta = {...req.body, data: undefined} as FeatureMeta // TODO: Must also include invoiceTxn
267
+ let meta = {...req.body, data: undefined, topic: feature.topic, date: new Date().getTime()} as FeatureMeta // TODO: Must also include invoiceTxn
271
268
 
272
269
  if (process.env.USE_DEFAULT_COMPANY) {
273
270
  meta.company = 'DEFAULT_COMPANY'
@@ -280,15 +277,6 @@ const apiHandler = async (req: ExpressRequest, res: ExpressResponse) => {
280
277
  meta,
281
278
  data: req.body.data,
282
279
  params: req.params,
283
-
284
- // To be removed 1
285
- date: new Date().getTime(),
286
-
287
- // To be removed 2
288
- topic,
289
- feature: req.body.feature,
290
- user: req.body.user,
291
- txn: meta.txn,
292
280
  }
293
281
 
294
282
  const appReq: Req = {
@@ -306,29 +294,31 @@ const apiHandler = async (req: ExpressRequest, res: ExpressResponse) => {
306
294
 
307
295
  if (method === "get") {
308
296
  try {
309
- // await feature.handler({...req, headers: req.headers, body: {...req.body, topic} }, res)
310
- // req.body = {...req.body, topic}
311
- await (feature.handler as Query)(appReq, appRes)
297
+ const data = await feature.handler(message, appReq, appRes)
298
+ if (!appRes.headersSent)
299
+ res.json({ data })
312
300
  } catch (err: any) {
313
301
  if (process.env.DEBUG_MODE)
314
- console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err)
302
+ console.error('Error: Feature: ', message.meta.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err)
315
303
  throw {status: 500, message: "Failed to GET feature", ...err}
316
304
  }
317
305
  } else if (method !== "get" && feature.bypass) {
318
306
  try {
319
- await (feature.handler as Command)(topic, message, appReq, appRes)
307
+ const data = await feature.handler(message, appReq, appRes)
308
+ if (!appRes.headersSent)
309
+ res.json({ data, event: message.meta.feature, txn: message.meta.txn, bypass: true })
320
310
  } catch (err: any) {
321
311
  if (process.env.DEBUG_MODE)
322
- console.error('Error: Feature: ', req.body?.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err)
312
+ console.error('Error: Feature: ', message.meta.feature ?? (featureless ? `Featureless Api:${featureless.api}` : undefined), err)
323
313
  throw {status: 500, message: "Failed to POST feature", ...err}
324
314
  }
325
315
  } else {
326
316
  try {
327
- await Kafka.send(`Features.${topic}`, message, message.user, { compression: Kafka.CompressionTypes.GZIP })
317
+ await Kafka.send(`Features.${message.meta.topic}`, message, message.meta.user, { compression: Kafka.CompressionTypes.GZIP })
328
318
  } catch {
329
319
  throw {status: 500, message: "Failed to queue the transaction"}
330
320
  }
331
- appRes.json({ data: { txn: message.txn, success: true, status: "Queued", featureQueued: true } })
321
+ appRes.json({ data: { txn: message.meta.txn, success: true, status: "Queued", featureQueued: true } })
332
322
  }
333
323
  } catch (err: any) {
334
324
  if (process.env.DEBUG_MODE)
@@ -365,9 +355,9 @@ async function registerHandler(features: Record<string, FeatureEntry>) {
365
355
  continue
366
356
 
367
357
  if (featureName.startsWith("flows.") && !featureName.startsWith("flows.query.") && !featureName.startsWith("flows.command.") && !featureName.startsWith("flows.designs."))
368
- feature.handler = (await import(`${new URL(`src/flows/query.js`, PROJECT_ROOT_URL).toString().replace('file://', '')}`)).FEATURE_HANDLERS[handler]
358
+ feature.handler = (await import(`${new URL(`src/flows/query.ts`, PROJECT_ROOT_URL).toString().replace('file://', '')}`)).FEATURE_HANDLERS[handler]
369
359
  else
370
- feature.handler = (await import(`${new URL(`${featurePath}.js`, PROJECT_ROOT_URL).toString().replace('file://', '')}`))[handler]
360
+ feature.handler = (await import(`${new URL(`${featurePath}.ts`, PROJECT_ROOT_URL).toString().replace('file://', '')}`))[handler]
371
361
  }
372
362
  }
373
363
  function registerApi() {
@@ -419,31 +409,31 @@ export const start = async (app: ExpressApplication, url: URL, file: string) =>
419
409
  for (let topic of kafkaTopics) {
420
410
  const groupId = ''
421
411
  Kafka.receive<FeatureMessage>(`Features.${topic}`, groupId, async (topic: string, message: FeatureMessage) => {
412
+ topic = topic.replace(/^.*Features./,'')
422
413
  if (Utils.isEmpty(message.meta?.company))
423
414
  message.meta = { ...message.meta, company: "GLOBAL", outlet: "GLOBAL" }
424
- if (!message?.feature)
415
+ if (!message.meta.feature)
425
416
  return
426
417
 
427
418
  const timer = (ms: number) => new Promise(res => setTimeout(res, ms)) // A promise that resolves after "ms" Milliseconds
428
- topic = topic.replace(/^.*Features./,'')
429
419
  while (true) {
430
420
  try {
431
- // const iso_date = new Date(message.date).toISOString()
421
+ // const iso_date = new Date(message.meta.date).toISOString()
432
422
  // // TODO: use $useChunks: [] once support is added in dip insert
433
- // await Dip.insert(globalMeta, `users.txns`, { _id: `${message.user}_${iso_date}_${message.txn}`, user: message.user, feature: message.feature, date: message.date, created: message.date, updated: message.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
423
+ // await Dip.insert(globalMeta, `users.txns`, { _id: `${message.meta.user}_${iso_date}_${message.meta.txn}`, user: message.meta.user, feature: message.meta.feature, date: message.meta.date, created: message.meta.date, updated: message.meta.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
434
424
  // TODO: Avoid duplicate processing
435
- const feature = features[message.feature]
425
+ const feature = features[message.meta.feature]
436
426
  if (!feature) {
437
- console.warn(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`)
438
- throw new Error(`Feature ${message.feature} missing, in executing handler during Kafka.receive(topic: ${topic})`)
427
+ console.warn(`Feature ${message.meta.feature} missing, in executing handler during Kafka.receive(topic: ${message.meta.topic})`)
428
+ throw new Error(`Feature ${message.meta.feature} missing, in executing handler during Kafka.receive(topic: ${message.meta.topic})`)
439
429
  }
440
- await (feature.handler as Command)(topic, message)
441
- await Dip.insert(globalMeta, "Features.txns", {_id: message.txn, status: "Processed"}, {idempotent: true})
430
+ await feature.handler(message)
431
+ await Dip.insert(globalMeta, "Features.txns", {_id: message.meta.txn, status: "Processed"}, {idempotent: true})
442
432
  break
443
433
  } catch(err) {
444
434
  if (process.env.DEBUG_MODE)
445
- console.log('Error: Feature: ', message.feature, err)
446
- console.warn(`Feature: ${message.feature}, error in executing handler during Kafka.receive(topic: ${topic})`)
435
+ console.log('Error: Feature: ', message.meta.feature, err)
436
+ console.warn(`Feature: ${message.meta.feature}, error in executing handler during Kafka.receive(topic: ${message.meta.topic})`)
447
437
  }
448
438
  await timer(10000);
449
439
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "corebasic",
3
3
  "type": "module",
4
- "version": "1.0.240",
4
+ "version": "1.0.242",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "./index.ts",