Skip to content

Commit 4e34ddc

Browse files
author
Gerald Baulig
committed
fix(cfg): add importExt to config - replaces .ext of import path
For some environments you have to replace the extention from .cjs to .js or else, e.g. for test
1 parent 93f3174 commit 4e34ddc

9 files changed

+107
-71
lines changed

.mocharc.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"extension": [
3-
"ts"
3+
"ts"
44
],
55
"spec": "test/**/*.spec.ts",
66
"require": "ts-node/register",
77
"node-option": [
8-
"experimental-specifier-resolution=node",
9-
"loader=ts-node/esm"
8+
"experimental-specifier-resolution=node",
9+
"loader=ts-node/esm"
1010
]
1111
}

Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
### Build
2-
FROM node:22.11.0-alpine3.20 as build
2+
FROM node:22.11.0-alpine3.20 AS build
33
ENV NO_UPDATE_NOTIFIER=true
44

55
USER node
@@ -17,7 +17,7 @@ RUN npm run build
1717

1818

1919
### Deployment
20-
FROM node:22.11.0-alpine3.20 as deployment
20+
FROM node:22.11.0-alpine3.20 AS deployment
2121

2222
ENV NO_UPDATE_NOTIFIER=true
2323

buildImage.bash

+10-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
docker build -t restorecommerce/scheduling-srv .
1+
#!/bin/bash
2+
3+
SERVICE_NAME="scheduling-srv"
4+
5+
DOCKER_BUILDKIT=1 docker build \
6+
--tag restorecommerce/$SERVICE_NAME \
7+
-f ./Dockerfile \
8+
--cache-from restorecommerce/$SERVICE_NAME \
9+
--build-arg APP_HOME=/home/node/$SERVICE_NAME \
10+
.

buildImageBuildkit.bash

-10
This file was deleted.

cfg/config_test.json

+5
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@
266266
"prefix": "acs:"
267267
}
268268
},
269+
"externalJobs": {
270+
"sourcePath": "./lib/external-jobs/",
271+
"importPath": "./external-jobs/",
272+
"importExt": ".js"
273+
},
269274
"errors": {
270275
"USER_NOT_LOGGED_IN": {
271276
"code": "401",

src/worker.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import path from 'path';
12
import * as _ from 'lodash-es';
23
import * as chassis from '@restorecommerce/chassis-srv';
34
import { Events, Topic, registerProtoMeta } from '@restorecommerce/kafka-client';
@@ -349,10 +350,11 @@ export class Worker {
349350
if (externalJobFiles?.length > 0) {
350351
await Promise.allSettled(externalJobFiles.map(async (externalFile) => {
351352
if (externalFile.endsWith('.js') || externalFile.endsWith('.cjs')) {
352-
const importPath = [
353+
const importExt = cfg.get('externalJobs:importExt') ?? path.extname(externalFile);
354+
const importPath = './' + path.join(
353355
process.env.EXTERNAL_JOBS_REQUIRE_DIR ?? cfg.get('externalJobs:importPath') ?? './external-jobs/',
354-
externalFile
355-
].join('').replace(/\.cjs$/, '.js');
356+
externalFile.replace(/\.\w+$/, importExt),
357+
);
356358
try {
357359
const fileImport = await import(importPath);
358360
// check for double default
@@ -361,7 +363,7 @@ export class Worker {
361363
} else {
362364
await fileImport.default(cfg, logger, events, runWorker);
363365
}
364-
this.logger?.info('imported:', importPath);
366+
this.logger?.info('Imported:', importPath);
365367
}
366368
catch (err: any) {
367369
this.logger?.error(`Error scheduling external job ${importPath}`, { err });

test/grpc_acs.spec.ts

+33-30
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { Worker } from '../src/worker.js';
55
import { Topic } from '@restorecommerce/kafka-client';
66
import { createChannel, createClient } from '@restorecommerce/grpc-client';
77
import {
8-
JobServiceClient as SchedulingServiceClient,
98
JobServiceDefinition as SchedulingServiceDefinition,
9+
JobServiceClient as SchedulingServiceClient,
1010
JobOptions_Priority,
1111
Backoff_Type,
1212
JobReadRequest,
@@ -25,7 +25,8 @@ import {
2525
jobPolicySetRQ,
2626
permitJobRule,
2727
validateJobDonePayload,
28-
cfg
28+
cfg,
29+
getSchedulingServiceClient
2930
} from './utils.js';
3031
import { updateConfig } from '@restorecommerce/acs-client';
3132
import * as _ from 'lodash-es';
@@ -110,9 +111,10 @@ const proto: any = ProtoUtils.getProtoFromPkgDefinition(
110111
pkgDef
111112
);
112113

113-
const mockServer = new GrpcMockServer(cfg.get('client:acs-srv:address'));
114-
const startGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
114+
let mockServerACS: GrpcMockServer;
115+
const startACSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
115116
// create mock implementation based on the method name and output
117+
mockServerACS = new GrpcMockServer(cfg.get('client:acs-srv:address'));
116118
const implementations = {
117119
isAllowed: (call: any, callback: any) => {
118120
const isAllowedResponse = methodWithOutput.filter(e => e.method === 'IsAllowed');
@@ -141,30 +143,31 @@ const startGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
141143
}
142144
};
143145
try {
144-
mockServer.addService(PROTO_PATH, PKG_NAME, SERVICE_NAME, implementations, {
146+
mockServerACS.addService(PROTO_PATH, PKG_NAME, SERVICE_NAME, implementations, {
145147
includeDirs: ['node_modules/@restorecommerce/protos/'],
146148
keepCase: true,
147149
longs: String,
148150
enums: String,
149151
defaults: true,
150152
oneofs: true
151153
});
152-
await mockServer.start();
153-
logger.info(`Mock ACS Server started on ${mockServer.serverAddress}`);
154+
await mockServerACS.start();
155+
logger.info(`Mock ACS Server started on ${mockServerACS.serverAddress}`);
154156
} catch (err) {
155157
logger.error('Error starting mock ACS server', err);
156158
}
159+
return mockServerACS;
157160
};
158161

159162
const IDS_PROTO_PATH = 'io/restorecommerce/user.proto';
160163
const IDS_PKG_NAME = 'io.restorecommerce.user';
161164
const IDS_SERVICE_NAME = 'UserService';
162165

163-
const mockServerIDS = new GrpcMockServer(cfg.get('client:user:address'));
164-
165166
// Mock server for ids - findByToken
167+
let mockServerIDS: GrpcMockServer;
166168
const startIDSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
167169
// create mock implementation based on the method name and output
170+
mockServerIDS = new GrpcMockServer(cfg.get('client:user:address'));
168171
const implementations = {
169172
findByToken: (call: any, callback: any) => {
170173
if (call.request.token === 'admin_token') {
@@ -187,16 +190,17 @@ const startIDSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
187190
} catch (err) {
188191
logger.error('Error starting mock IDS server', err);
189192
}
193+
return mockServerIDS;
190194
};
191195

192196

193-
const stopGrpcMockServer = async () => {
194-
await mockServer.stop();
197+
const stopACSGrpcMockServer = async () => {
198+
await mockServerACS?.stop();
195199
logger.info('Mock ACS Server closed successfully');
196200
};
197201

198202
const stopIDSGrpcMockServer = async () => {
199-
await mockServerIDS.stop();
203+
await mockServerIDS?.stop();
200204
logger.info('Mock IDS Server closed successfully');
201205
};
202206

@@ -228,7 +232,7 @@ describe(`testing scheduling-srv ${testSuffix}: gRPC`, () => {
228232
// start acs mock service with PERMIT rule
229233
jobPolicySetRQ.policy_sets![0]!.policies![0]!.effect = Effect.PERMIT;
230234
jobPolicySetRQ.policy_sets![0]!.policies![0]!.rules = [permitJobRule];
231-
await startGrpcMockServer([
235+
await startACSGrpcMockServer([
232236
{
233237
method: 'WhatIsAllowed',
234238
output: jobPolicySetRQ
@@ -265,13 +269,7 @@ describe(`testing scheduling-srv ${testSuffix}: gRPC`, () => {
265269
// store user with tokens and role associations to Redis index `db-findByToken`
266270
await tokenRedisClient.set('admin-token', JSON.stringify(acsSubject));
267271

268-
const schedulingClientCfg = cfg.get('client:schedulingClient');
269-
if (schedulingClientCfg) {
270-
grpcSchedulingSrv = createClient({
271-
...schedulingClientCfg,
272-
logger
273-
}, SchedulingServiceDefinition, createChannel(schedulingClientCfg.address));
274-
}
272+
grpcSchedulingSrv = getSchedulingServiceClient(logger);
275273
const toDelete = (await grpcSchedulingSrv.read(JobReadRequest.fromPartial({ subject }), {})).total_count;
276274
const offset = await jobEvents.$offset(-1);
277275

@@ -289,19 +287,24 @@ describe(`testing scheduling-srv ${testSuffix}: gRPC`, () => {
289287
}
290288
});
291289
afterEach(async () => {
292-
await jobEvents.removeAllListeners('queuedJob');
293-
await jobEvents.removeAllListeners('jobsCreated');
294-
await jobEvents.removeAllListeners('jobsDeleted');
290+
await Promise.allSettled([
291+
jobEvents.removeAllListeners('queuedJob'),
292+
jobEvents.removeAllListeners('jobsCreated'),
293+
jobEvents.removeAllListeners('jobsDeleted'),
294+
]);
295295
});
296296
after(async function (): Promise<any> {
297297
this.timeout(20000);
298-
await stopGrpcMockServer();
299-
await stopIDSGrpcMockServer();
300-
await jobEvents.removeAllListeners('queuedJob');
301-
await jobEvents.removeAllListeners('jobsCreated');
302-
await jobEvents.removeAllListeners('jobsDeleted');
303-
await worker.schedulingService.clear();
304-
await worker.stop();
298+
await Promise.allSettled([
299+
stopACSGrpcMockServer(),
300+
stopIDSGrpcMockServer(),
301+
jobEvents.removeAllListeners('queuedJob'),
302+
jobEvents.removeAllListeners('jobsCreated'),
303+
jobEvents.removeAllListeners('jobsDeleted'),
304+
worker.schedulingService.clear(),
305+
]).then(
306+
worker.stop
307+
);
305308
});
306309
describe(`create a one-time job ${testSuffix}`, function postJob(): void {
307310
this.timeout(30000);

test/kafka_acs.spec.ts

+29-21
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ const proto: any = ProtoUtils.getProtoFromPkgDefinition(
102102
pkgDef
103103
);
104104

105-
const mockServer = new GrpcMockServer(cfg.get('client:acs-srv:address'));
106-
107-
const startGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
105+
let mockServerACS: GrpcMockServer;
106+
const startACSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
108107
// create mock implementation based on the method name and output
108+
mockServerACS = new GrpcMockServer(cfg.get('client:acs-srv:address'));
109109
const implementations = {
110110
isAllowed: (call: any, callback: any) => {
111111
const isAllowedResponse = methodWithOutput.filter(e => e.method === 'IsAllowed');
@@ -120,30 +120,32 @@ const startGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
120120
}
121121
};
122122
try {
123-
mockServer.addService(PROTO_PATH, PKG_NAME, SERVICE_NAME, implementations, {
123+
mockServerACS.addService(PROTO_PATH, PKG_NAME, SERVICE_NAME, implementations, {
124124
includeDirs: ['node_modules/@restorecommerce/protos/'],
125125
keepCase: true,
126126
longs: String,
127127
enums: String,
128128
defaults: true,
129129
oneofs: true
130130
});
131-
await mockServer.start();
132-
logger.info(`Mock ACS Server started on ${mockServer.serverAddress}`);
131+
await mockServerACS.start();
132+
logger.info(`Mock ACS Server started on ${mockServerACS.serverAddress}`);
133133
} catch (err) {
134134
logger.error('Error starting mock ACS server', err);
135135
}
136+
mockServerACS;
136137
};
137138

138139
const IDS_PROTO_PATH = 'io/restorecommerce/user.proto';
139140
const IDS_PKG_NAME = 'io.restorecommerce.user';
140141
const IDS_SERVICE_NAME = 'UserService';
141142

142-
const mockServerIDS = new GrpcMockServer(cfg.get('client:user:address'));
143143

144144
// Mock server for ids - findByToken
145+
let mockServerIDS: GrpcMockServer;
145146
const startIDSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
146147
// create mock implementation based on the method name and output
148+
mockServerIDS = new GrpcMockServer(cfg.get('client:user:address'));
147149
const implementations = {
148150
findByToken: (call: any, callback: any) => {
149151
if (call.request.token === 'admin_token') {
@@ -166,15 +168,16 @@ const startIDSGrpcMockServer = async (methodWithOutput: MethodWithOutput[]) => {
166168
} catch (err) {
167169
logger.error('Error starting mock IDS server', err);
168170
}
171+
return mockServerIDS;
169172
};
170173

171-
const stopGrpcMockServer = async () => {
172-
await mockServer.stop();
174+
const stopACSGrpcMockServer = async () => {
175+
await mockServerACS?.stop();
173176
logger.info('Mock ACS Server closed successfully');
174177
};
175178

176179
const stopIDSGrpcMockServer = async () => {
177-
await mockServerIDS.stop();
180+
await mockServerIDS?.stop();
178181
logger.info('Mock IDS Server closed successfully');
179182
};
180183

@@ -197,7 +200,7 @@ describe(`testing scheduling-srv ${testSuffix}: Kafka`, async () => {
197200
jobPolicySetRQ!.policy_sets![0]!.policies![0]!.rules = [permitJobRule];
198201
// start mock acs-srv - needed for read operation since acs-client makes a req to acs-srv
199202
// to get applicable policies although acs-lookup is disabled
200-
await startGrpcMockServer([{ method: 'WhatIsAllowed', output: jobPolicySetRQ },
203+
await startACSGrpcMockServer([{ method: 'WhatIsAllowed', output: jobPolicySetRQ },
201204
{ method: 'IsAllowed', output: { decision: 'PERMIT' } }]);
202205
jobTopic = await worker.events.topic(JOB_EVENTS_TOPIC);
203206

@@ -256,19 +259,24 @@ describe(`testing scheduling-srv ${testSuffix}: Kafka`, async () => {
256259
}
257260
});
258261
afterEach(async () => {
259-
await jobTopic.removeAllListeners('queuedJob');
260-
await jobTopic.removeAllListeners('jobsCreated');
261-
await jobTopic.removeAllListeners('jobsDeleted');
262+
await Promise.allSettled([
263+
jobTopic.removeAllListeners('queuedJob'),
264+
jobTopic.removeAllListeners('jobsCreated'),
265+
jobTopic.removeAllListeners('jobsDeleted'),
266+
]);
262267
});
263268
after(async function (): Promise<any> {
264269
this.timeout(20000);
265-
await stopGrpcMockServer();
266-
await stopIDSGrpcMockServer();
267-
await jobTopic.removeAllListeners('queuedJob');
268-
await jobTopic.removeAllListeners('jobsCreated');
269-
await jobTopic.removeAllListeners('jobsDeleted');
270-
await worker.schedulingService.clear();
271-
await worker.stop();
270+
await Promise.allSettled([
271+
stopACSGrpcMockServer(),
272+
stopIDSGrpcMockServer(),
273+
jobTopic.removeAllListeners('queuedJob'),
274+
jobTopic.removeAllListeners('jobsCreated'),
275+
jobTopic.removeAllListeners('jobsDeleted'),
276+
worker.schedulingService.clear(),
277+
]).then(
278+
worker.stop
279+
);
272280
});
273281
describe('create a one-time job', function postJob(): void {
274282
this.timeout(15000);

test/utils.ts

+19
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,28 @@ import { Logger } from 'winston';
55
import { PolicySetRQResponse } from '@restorecommerce/acs-client';
66
import { Effect } from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/rule.js';
77
import { createServiceConfig } from '@restorecommerce/service-config';
8+
import { createChannel, createClient } from '@restorecommerce/grpc-client';
9+
import {
10+
JobServiceDefinition as SchedulingServiceDefinition,
11+
JobServiceClient as SchedulingServiceClient,
12+
} from '@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/job.js';
813

914
export const cfg = createServiceConfig(process.cwd());
1015

16+
let _client: SchedulingServiceClient
17+
export function getSchedulingServiceClient(logger: Logger) {
18+
const schedulingClientCfg = cfg.get('client:schedulingClient');
19+
_client = createClient(
20+
{
21+
...schedulingClientCfg,
22+
logger
23+
},
24+
SchedulingServiceDefinition,
25+
createChannel(schedulingClientCfg.address)
26+
);
27+
return _client;
28+
}
29+
1130
export function validateScheduledJob(job: any, expectedSchedule: string, logger: Logger): void {
1231
should.exist(job.data);
1332
should.exist(job.data.payload);

0 commit comments

Comments
 (0)