corebasic 1.0.265 → 1.0.266

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.
@@ -48,6 +48,19 @@ const getFeatureMethod = (api) => api.split(' ')[0].toLowerCase();
48
48
  const getFeatureUrl = (api) => { const url = api.split(' ')[1]; if (!url)
49
49
  throw new Error(`Invalid Feature url in api: ${api}`); return url; };
50
50
  const getFeature = (name) => features[name];
51
+ function getLeaf(feature, req) {
52
+ if (req) {
53
+ for (let key in features) {
54
+ const feature = features[key];
55
+ if (feature.featureless && feature.api === req.method.toUpperCase() + ' ' + req.path)
56
+ return key.split(/\.(query|command)\./)[0];
57
+ }
58
+ return "NOLEAF"; // This code will never be reached as apiHandler()'s prior code ensures req is valid and there exists a feature. Included to silence ./tsc
59
+ }
60
+ else {
61
+ return feature.split(/\.(query|command)\./)[0];
62
+ }
63
+ }
51
64
  const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN";
52
65
  const SERVICE_ACCESS_TOKEN = jwt.sign({ app: process.env.APP_DEPLOYMENT_NAME }, DEPLOY_TOKEN_SECRET, { expiresIn: '365d' });
53
66
  const NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN = await (async () => { if (process.env.NDCURVE_DEVELOPER_SERVICE)
@@ -81,8 +94,8 @@ let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000';
81
94
  export const get = (feature) => {
82
95
  const throwError = () => { throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`); };
83
96
  const baseFeature = getFeature(feature);
84
- let { api, service = SERVICE_ADDRESS, topic } = baseFeature ? baseFeature : (SLYP_FEATURES_LIST[feature] ?? throwError());
85
- return { api, service, topic };
97
+ let { api, service = SERVICE_ADDRESS } = baseFeature ? baseFeature : (SLYP_FEATURES_LIST[feature] ?? throwError());
98
+ return { api, service };
86
99
  };
87
100
  export const send = async (meta, feature, data, params) => {
88
101
  const throwError = () => { throw new Error(`Feature ${feature} not found in internal or external list during inter feature call`); };
@@ -134,7 +147,7 @@ let appids = {};
134
147
  async function announce() {
135
148
  let exp_features = {};
136
149
  for (let [key, { api, subscribe }] of Object.entries(features))
137
- exp_features[key] = { api, service: SERVICE_ADDRESS, subscribe, topic: "" };
150
+ exp_features[key] = { api, service: SERVICE_ADDRESS, subscribe };
138
151
  await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, _channel) => {
139
152
  let { uid, ...msg } = JSON.parse(message);
140
153
  if (uid !== appId && !appids[uid]) {
@@ -172,11 +185,12 @@ const apiHandler = async (req, res) => {
172
185
  throw { status: 404, message: `Resource not found. Feature ${req.body.feature} not available.` };
173
186
  }
174
187
  feature = feature; // typescript validation with !
188
+ const leaf = getLeaf(req.body.feature, req);
175
189
  let params = getFeatureUrl(feature.api).split("/").filter((item) => item.startsWith(":")).map((item) => item.replace(":", ""));
176
190
  for (let param of params)
177
191
  if (!req.params[param])
178
192
  throw { status: 404, message: "Resource not found. One or more url parameter not specified." };
179
- let meta = { ...req.body, data: undefined, topic: feature.topic, date: new Date().getTime() }; // TODO: Must also include invoiceTxn
193
+ let meta = { ...req.body, data: undefined, topic: leaf, date: new Date().getTime() }; // TODO: Must also include invoiceTxn
180
194
  if (process.env.USE_DEFAULT_COMPANY) {
181
195
  meta.company = 'DEFAULT_COMPANY';
182
196
  meta.outlet = 'DEFAULT_OUTLET';
@@ -318,7 +332,7 @@ export const start = async (app, url, file) => {
318
332
  app.get('/features', async (_req, res) => {
319
333
  let exp_features = {};
320
334
  for (let [key, { api }] of Object.entries(features))
321
- exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: '', topic: '' };
335
+ exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: '' };
322
336
  if (process.env.LOAD_LOCAL_FEATURES) {
323
337
  for (let key in SLYP_FEATURES_LIST)
324
338
  SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN }; // TODO: CRITICAL: Security issue
@@ -328,12 +342,13 @@ export const start = async (app, url, file) => {
328
342
  // Registering features
329
343
  await registerFeatures(features);
330
344
  // Get unique kafka topics list from features
331
- let kafkaTopics = [];
345
+ let kafkaTopics = new Set();
332
346
  for (const name in features) {
333
347
  let feature = features[name];
334
348
  if (feature.api.split(' ')[0]?.toLowerCase() === "get")
335
349
  continue;
336
- kafkaTopics = [...new Set(kafkaTopics.concat([feature.topic]))];
350
+ const leaf = getLeaf(name);
351
+ kafkaTopics.add(leaf);
337
352
  }
338
353
  // Subscribe to each topic
339
354
  for (let topic of kafkaTopics) {
@@ -442,7 +457,7 @@ async function subscribe() {
442
457
  const publisherFeature = getFeature(publisher);
443
458
  if (subscribed_consumers[consumer] || !publisherFeature)
444
459
  continue;
445
- let { topic } = publisherFeature;
460
+ let topic = getLeaf(publisher);
446
461
  let kafka_consumer_promise = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
447
462
  topic = topic.replace(/^.*Features./, '');
448
463
  const timer = (ms) => new Promise(res => setTimeout(res, ms)); // A promise that resolves after "ms" Milliseconds
package/libs/features.ts CHANGED
@@ -141,11 +141,10 @@ type RemoteFeatureEntry = {
141
141
  api: string
142
142
  service: string
143
143
  subscribe: string
144
- topic: string
145
144
  headers?: {JWT?: string, service?: boolean, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN?: string}
146
145
  }
147
146
 
148
- type FeatureEntry = { api: string, topic: string, bypass: boolean, featureless: boolean, subscribe: string, handler: Handler<unknown, unknown, keyof Schema>, opt?: string[], mut?: string[] }
147
+ type FeatureEntry = { api: string, bypass: boolean, featureless: boolean, subscribe: string, handler: Handler<unknown, unknown, keyof Schema>, opt?: string[], mut?: string[] }
149
148
 
150
149
 
151
150
  let features: Record<string, FeatureEntry> = {}
@@ -157,6 +156,20 @@ const getFeatureMethod = (api: string): HttpMethod => api.split(' ')[0]!.toLower
157
156
  const getFeatureUrl = (api: string): string => { const url = api.split(' ')[1]; if (!url) throw new Error(`Invalid Feature url in api: ${api}`); return url}
158
157
  const getFeature = (name: string): FeatureEntry | undefined => features[name]
159
158
 
159
+ function getLeaf(feature: string, req?: ExpressRequest): string {
160
+ if (req) {
161
+ for(let key in features) {
162
+ const feature = features[key]!
163
+ if (feature.featureless && feature.api === req.method.toUpperCase() + ' ' + req.path)
164
+ return key.split(/\.(query|command)\./)[0]!
165
+ }
166
+ return "NOLEAF" // This code will never be reached as apiHandler()'s prior code ensures req is valid and there exists a feature. Included to silence ./tsc
167
+ } else {
168
+ return feature.split(/\.(query|command)\./)[0]!
169
+ }
170
+ }
171
+
172
+
160
173
  const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN"
161
174
  const SERVICE_ACCESS_TOKEN = jwt.sign({app: process.env.APP_DEPLOYMENT_NAME}, DEPLOY_TOKEN_SECRET, { expiresIn: '365d' });
162
175
 
@@ -189,8 +202,8 @@ let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000'
189
202
  export const get = (feature: string) => {
190
203
  const throwError = () => {throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`)}
191
204
  const baseFeature: FeatureEntry | undefined = getFeature(feature)
192
- let {api, service = SERVICE_ADDRESS, topic} = baseFeature ? baseFeature as FeatureEntry & {service: string} : (SLYP_FEATURES_LIST[feature] ?? throwError())
193
- return {api, service, topic}
205
+ let {api, service = SERVICE_ADDRESS} = baseFeature ? baseFeature as FeatureEntry & {service: string} : (SLYP_FEATURES_LIST[feature] ?? throwError())
206
+ return {api, service}
194
207
  }
195
208
 
196
209
 
@@ -251,7 +264,7 @@ let appids: Record<string, boolean> = {}
251
264
  async function announce() {
252
265
  let exp_features: Record<string, RemoteFeatureEntry> = {}
253
266
  for (let [key, {api,subscribe}] of Object.entries(features))
254
- exp_features[key] = {api, service: SERVICE_ADDRESS, subscribe, topic: ""}
267
+ exp_features[key] = {api, service: SERVICE_ADDRESS, subscribe}
255
268
 
256
269
  await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, _channel) => {
257
270
  let {uid, ...msg} = JSON.parse(message) as Record<string, RemoteFeatureEntry> & {uid: string}
@@ -297,12 +310,14 @@ const apiHandler = async (req: ExpressRequest, res: ExpressResponse) => {
297
310
 
298
311
  feature = feature! // typescript validation with !
299
312
 
313
+ const leaf = getLeaf(req.body.feature, req)
314
+
300
315
  let params = getFeatureUrl(feature.api).split("/").filter((item: string) => item.startsWith(":")).map((item: string) => item.replace(":", ""))
301
316
  for (let param of params)
302
317
  if (!req.params[param])
303
318
  throw { status: 404, message: "Resource not found. One or more url parameter not specified." }
304
319
 
305
- let meta = {...req.body, data: undefined, topic: feature.topic, date: new Date().getTime()} as FeatureMeta // TODO: Must also include invoiceTxn
320
+ let meta = {...req.body, data: undefined, topic: leaf, date: new Date().getTime()} as FeatureMeta // TODO: Must also include invoiceTxn
306
321
 
307
322
  if (process.env.USE_DEFAULT_COMPANY) {
308
323
  meta.company = 'DEFAULT_COMPANY'
@@ -457,7 +472,7 @@ export const start = async (app: ExpressApplication, url: URL, file: string) =>
457
472
  app.get('/features', async (_req: ExpressRequest, res: ExpressResponse) => {
458
473
  let exp_features: Record<string, RemoteFeatureEntry> = {}
459
474
  for (let [key, {api}] of Object.entries(features))
460
- exp_features[key] = {api, service: `${SERVICE_ADDRESS}`, subscribe: '', topic: ''}
475
+ exp_features[key] = {api, service: `${SERVICE_ADDRESS}`, subscribe: ''}
461
476
  if (process.env.LOAD_LOCAL_FEATURES) {
462
477
  for (let key in SLYP_FEATURES_LIST)
463
478
  SLYP_FEATURES_LIST[key]!.headers = {JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN} // TODO: CRITICAL: Security issue
@@ -469,12 +484,13 @@ export const start = async (app: ExpressApplication, url: URL, file: string) =>
469
484
  await registerFeatures(features)
470
485
 
471
486
  // Get unique kafka topics list from features
472
- let kafkaTopics: string[] = []
487
+ let kafkaTopics = new Set<string>()
473
488
  for (const name in features) {
474
489
  let feature = features[name]!
475
490
  if (feature.api.split(' ')[0]?.toLowerCase() === "get" )
476
491
  continue
477
- kafkaTopics = [... new Set(kafkaTopics.concat([feature.topic]))]
492
+ const leaf = getLeaf(name)
493
+ kafkaTopics.add(leaf)
478
494
  }
479
495
 
480
496
  // Subscribe to each topic
@@ -587,7 +603,7 @@ async function subscribe() {
587
603
  if (subscribed_consumers[consumer] || !publisherFeature)
588
604
  continue
589
605
 
590
- let {topic} = publisherFeature
606
+ let topic = getLeaf(publisher)
591
607
 
592
608
  let kafka_consumer_promise = Kafka.receive<FeatureMessage>(`Features.${topic}`, consumer, async (topic: string, message: FeatureMessage) => {
593
609
  topic = topic.replace(/^.*Features./,'')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "corebasic",
3
3
  "type": "module",
4
- "version": "1.0.265",
4
+ "version": "1.0.266",
5
5
  "description": "",
6
6
  "main": "dist/index.js",
7
7
  "types": "./index.ts",