corebasic 1.0.265 → 1.0.267
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/features.js +25 -8
- package/libs/features.ts +29 -10
- package/package.json +1 -1
package/dist/libs/features.js
CHANGED
|
@@ -48,6 +48,21 @@ 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 (feature)
|
|
53
|
+
return feature.split(/\.(query|command)\./)[0];
|
|
54
|
+
if (req) {
|
|
55
|
+
for (let key in features) {
|
|
56
|
+
const feature = features[key];
|
|
57
|
+
if (feature.featureless && feature.api === req.method.toUpperCase() + ' ' + req.path)
|
|
58
|
+
return key.split(/\.(query|command)\./)[0];
|
|
59
|
+
}
|
|
60
|
+
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
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
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
|
|
64
|
+
}
|
|
65
|
+
}
|
|
51
66
|
const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN";
|
|
52
67
|
const SERVICE_ACCESS_TOKEN = jwt.sign({ app: process.env.APP_DEPLOYMENT_NAME }, DEPLOY_TOKEN_SECRET, { expiresIn: '365d' });
|
|
53
68
|
const NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN = await (async () => { if (process.env.NDCURVE_DEVELOPER_SERVICE)
|
|
@@ -81,8 +96,8 @@ let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000';
|
|
|
81
96
|
export const get = (feature) => {
|
|
82
97
|
const throwError = () => { throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`); };
|
|
83
98
|
const baseFeature = getFeature(feature);
|
|
84
|
-
let { api, service = SERVICE_ADDRESS
|
|
85
|
-
return { api, service
|
|
99
|
+
let { api, service = SERVICE_ADDRESS } = baseFeature ? baseFeature : (SLYP_FEATURES_LIST[feature] ?? throwError());
|
|
100
|
+
return { api, service };
|
|
86
101
|
};
|
|
87
102
|
export const send = async (meta, feature, data, params) => {
|
|
88
103
|
const throwError = () => { throw new Error(`Feature ${feature} not found in internal or external list during inter feature call`); };
|
|
@@ -134,7 +149,7 @@ let appids = {};
|
|
|
134
149
|
async function announce() {
|
|
135
150
|
let exp_features = {};
|
|
136
151
|
for (let [key, { api, subscribe }] of Object.entries(features))
|
|
137
|
-
exp_features[key] = { api, service: SERVICE_ADDRESS, subscribe
|
|
152
|
+
exp_features[key] = { api, service: SERVICE_ADDRESS, subscribe };
|
|
138
153
|
await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, _channel) => {
|
|
139
154
|
let { uid, ...msg } = JSON.parse(message);
|
|
140
155
|
if (uid !== appId && !appids[uid]) {
|
|
@@ -172,11 +187,12 @@ const apiHandler = async (req, res) => {
|
|
|
172
187
|
throw { status: 404, message: `Resource not found. Feature ${req.body.feature} not available.` };
|
|
173
188
|
}
|
|
174
189
|
feature = feature; // typescript validation with !
|
|
190
|
+
const leaf = getLeaf(req.body.feature, req);
|
|
175
191
|
let params = getFeatureUrl(feature.api).split("/").filter((item) => item.startsWith(":")).map((item) => item.replace(":", ""));
|
|
176
192
|
for (let param of params)
|
|
177
193
|
if (!req.params[param])
|
|
178
194
|
throw { status: 404, message: "Resource not found. One or more url parameter not specified." };
|
|
179
|
-
let meta = { ...req.body, data: undefined, topic:
|
|
195
|
+
let meta = { ...req.body, data: undefined, topic: leaf, date: new Date().getTime() }; // TODO: Must also include invoiceTxn
|
|
180
196
|
if (process.env.USE_DEFAULT_COMPANY) {
|
|
181
197
|
meta.company = 'DEFAULT_COMPANY';
|
|
182
198
|
meta.outlet = 'DEFAULT_OUTLET';
|
|
@@ -318,7 +334,7 @@ export const start = async (app, url, file) => {
|
|
|
318
334
|
app.get('/features', async (_req, res) => {
|
|
319
335
|
let exp_features = {};
|
|
320
336
|
for (let [key, { api }] of Object.entries(features))
|
|
321
|
-
exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: ''
|
|
337
|
+
exp_features[key] = { api, service: `${SERVICE_ADDRESS}`, subscribe: '' };
|
|
322
338
|
if (process.env.LOAD_LOCAL_FEATURES) {
|
|
323
339
|
for (let key in SLYP_FEATURES_LIST)
|
|
324
340
|
SLYP_FEATURES_LIST[key].headers = { JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN }; // TODO: CRITICAL: Security issue
|
|
@@ -328,12 +344,13 @@ export const start = async (app, url, file) => {
|
|
|
328
344
|
// Registering features
|
|
329
345
|
await registerFeatures(features);
|
|
330
346
|
// Get unique kafka topics list from features
|
|
331
|
-
let kafkaTopics =
|
|
347
|
+
let kafkaTopics = new Set();
|
|
332
348
|
for (const name in features) {
|
|
333
349
|
let feature = features[name];
|
|
334
350
|
if (feature.api.split(' ')[0]?.toLowerCase() === "get")
|
|
335
351
|
continue;
|
|
336
|
-
|
|
352
|
+
const leaf = getLeaf(name);
|
|
353
|
+
kafkaTopics.add(leaf);
|
|
337
354
|
}
|
|
338
355
|
// Subscribe to each topic
|
|
339
356
|
for (let topic of kafkaTopics) {
|
|
@@ -442,7 +459,7 @@ async function subscribe() {
|
|
|
442
459
|
const publisherFeature = getFeature(publisher);
|
|
443
460
|
if (subscribed_consumers[consumer] || !publisherFeature)
|
|
444
461
|
continue;
|
|
445
|
-
let
|
|
462
|
+
let topic = getLeaf(publisher);
|
|
446
463
|
let kafka_consumer_promise = Kafka.receive(`Features.${topic}`, consumer, async (topic, message) => {
|
|
447
464
|
topic = topic.replace(/^.*Features./, '');
|
|
448
465
|
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,
|
|
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,23 @@ 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 (feature)
|
|
161
|
+
return feature.split(/\.(query|command)\./)[0]!
|
|
162
|
+
|
|
163
|
+
if (req) {
|
|
164
|
+
for(let key in features) {
|
|
165
|
+
const feature = features[key]!
|
|
166
|
+
if (feature.featureless && feature.api === req.method.toUpperCase() + ' ' + req.path)
|
|
167
|
+
return key.split(/\.(query|command)\./)[0]!
|
|
168
|
+
}
|
|
169
|
+
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
|
|
170
|
+
} else {
|
|
171
|
+
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
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
160
176
|
const DEPLOY_TOKEN_SECRET = process.env.DEPLOY_TOKEN_SECRET || "MY_SECRET_DEPLOY_TOKEN"
|
|
161
177
|
const SERVICE_ACCESS_TOKEN = jwt.sign({app: process.env.APP_DEPLOYMENT_NAME}, DEPLOY_TOKEN_SECRET, { expiresIn: '365d' });
|
|
162
178
|
|
|
@@ -189,8 +205,8 @@ let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000'
|
|
|
189
205
|
export const get = (feature: string) => {
|
|
190
206
|
const throwError = () => {throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`)}
|
|
191
207
|
const baseFeature: FeatureEntry | undefined = getFeature(feature)
|
|
192
|
-
let {api, service = SERVICE_ADDRESS
|
|
193
|
-
return {api, service
|
|
208
|
+
let {api, service = SERVICE_ADDRESS} = baseFeature ? baseFeature as FeatureEntry & {service: string} : (SLYP_FEATURES_LIST[feature] ?? throwError())
|
|
209
|
+
return {api, service}
|
|
194
210
|
}
|
|
195
211
|
|
|
196
212
|
|
|
@@ -251,7 +267,7 @@ let appids: Record<string, boolean> = {}
|
|
|
251
267
|
async function announce() {
|
|
252
268
|
let exp_features: Record<string, RemoteFeatureEntry> = {}
|
|
253
269
|
for (let [key, {api,subscribe}] of Object.entries(features))
|
|
254
|
-
exp_features[key] = {api, service: SERVICE_ADDRESS, subscribe
|
|
270
|
+
exp_features[key] = {api, service: SERVICE_ADDRESS, subscribe}
|
|
255
271
|
|
|
256
272
|
await Messaging.subscribe(`${process.env.REDIS_CHANNEL_PREFIX}_SLYP_FEATURES_LIST`, async (message, _channel) => {
|
|
257
273
|
let {uid, ...msg} = JSON.parse(message) as Record<string, RemoteFeatureEntry> & {uid: string}
|
|
@@ -297,12 +313,14 @@ const apiHandler = async (req: ExpressRequest, res: ExpressResponse) => {
|
|
|
297
313
|
|
|
298
314
|
feature = feature! // typescript validation with !
|
|
299
315
|
|
|
316
|
+
const leaf = getLeaf(req.body.feature, req)
|
|
317
|
+
|
|
300
318
|
let params = getFeatureUrl(feature.api).split("/").filter((item: string) => item.startsWith(":")).map((item: string) => item.replace(":", ""))
|
|
301
319
|
for (let param of params)
|
|
302
320
|
if (!req.params[param])
|
|
303
321
|
throw { status: 404, message: "Resource not found. One or more url parameter not specified." }
|
|
304
322
|
|
|
305
|
-
let meta = {...req.body, data: undefined, topic:
|
|
323
|
+
let meta = {...req.body, data: undefined, topic: leaf, date: new Date().getTime()} as FeatureMeta // TODO: Must also include invoiceTxn
|
|
306
324
|
|
|
307
325
|
if (process.env.USE_DEFAULT_COMPANY) {
|
|
308
326
|
meta.company = 'DEFAULT_COMPANY'
|
|
@@ -457,7 +475,7 @@ export const start = async (app: ExpressApplication, url: URL, file: string) =>
|
|
|
457
475
|
app.get('/features', async (_req: ExpressRequest, res: ExpressResponse) => {
|
|
458
476
|
let exp_features: Record<string, RemoteFeatureEntry> = {}
|
|
459
477
|
for (let [key, {api}] of Object.entries(features))
|
|
460
|
-
exp_features[key] = {api, service: `${SERVICE_ADDRESS}`, subscribe: ''
|
|
478
|
+
exp_features[key] = {api, service: `${SERVICE_ADDRESS}`, subscribe: ''}
|
|
461
479
|
if (process.env.LOAD_LOCAL_FEATURES) {
|
|
462
480
|
for (let key in SLYP_FEATURES_LIST)
|
|
463
481
|
SLYP_FEATURES_LIST[key]!.headers = {JWT: SERVICE_ACCESS_TOKEN, service: true, NDCURVE_DEVELOPER_LICENSE_ACCESS_TOKEN} // TODO: CRITICAL: Security issue
|
|
@@ -469,12 +487,13 @@ export const start = async (app: ExpressApplication, url: URL, file: string) =>
|
|
|
469
487
|
await registerFeatures(features)
|
|
470
488
|
|
|
471
489
|
// Get unique kafka topics list from features
|
|
472
|
-
let kafkaTopics
|
|
490
|
+
let kafkaTopics = new Set<string>()
|
|
473
491
|
for (const name in features) {
|
|
474
492
|
let feature = features[name]!
|
|
475
493
|
if (feature.api.split(' ')[0]?.toLowerCase() === "get" )
|
|
476
494
|
continue
|
|
477
|
-
|
|
495
|
+
const leaf = getLeaf(name)
|
|
496
|
+
kafkaTopics.add(leaf)
|
|
478
497
|
}
|
|
479
498
|
|
|
480
499
|
// Subscribe to each topic
|
|
@@ -587,7 +606,7 @@ async function subscribe() {
|
|
|
587
606
|
if (subscribed_consumers[consumer] || !publisherFeature)
|
|
588
607
|
continue
|
|
589
608
|
|
|
590
|
-
let
|
|
609
|
+
let topic = getLeaf(publisher)
|
|
591
610
|
|
|
592
611
|
let kafka_consumer_promise = Kafka.receive<FeatureMessage>(`Features.${topic}`, consumer, async (topic: string, message: FeatureMessage) => {
|
|
593
612
|
topic = topic.replace(/^.*Features./,'')
|