@clairejs/server 3.16.13 → 3.16.15

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/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  ## Change Log
2
2
 
3
- #### 3.16.13:
3
+ #### 3.16.15:
4
4
 
5
+ - fix CRUD order
6
+ - fix Job repo
5
7
  - fix AbstractJob
6
8
  - fix LocalJobScheduler
7
9
  - add abstract job model, default job repo
@@ -541,12 +541,7 @@ export class ModelRepository extends AbstractRepository {
541
541
  limit: queries?.limit,
542
542
  page: queries?.page,
543
543
  projection: finalProjection,
544
- order: queries?.order === "random"
545
- ? queries.order
546
- : queries?.order?.map((o) => {
547
- const key = Object.keys(o).find((k) => !!o[k]);
548
- return [key, o[key]];
549
- }),
544
+ order: queries?.order,
550
545
  },
551
546
  //-- page and limit does not affect nested queries
552
547
  this.getNestedQueries(queries));
@@ -1,17 +1,18 @@
1
+ import { IQueryProvider } from "@clairejs/orm";
1
2
  import { CustomJobInfo } from "./interfaces";
2
3
  export declare abstract class AbstractJobRepository {
3
4
  /**
4
5
  * Return all persisted jobs
5
6
  */
6
- abstract getJobs(): Promise<CustomJobInfo[]>;
7
+ abstract getJobs(query: IQueryProvider): Promise<CustomJobInfo[]>;
7
8
  /**
8
9
  * Save the job info and return a unique id
9
10
  * @param jobInfo The custom job info to save
10
11
  */
11
- abstract saveJob(jobInfo: CustomJobInfo): Promise<string>;
12
+ abstract saveJob(jobInfo: CustomJobInfo, query: IQueryProvider): Promise<string>;
12
13
  /**
13
14
  * Remove job info by id
14
15
  * @param jobId Unique id of job
15
16
  */
16
- abstract removeJobById(jobId: string): Promise<void>;
17
+ abstract removeJobById(jobId: string, query: IQueryProvider): Promise<void>;
17
18
  }
@@ -1,13 +1,15 @@
1
1
  import { AbstractLogger } from "@clairejs/core";
2
+ import { ITransaction, ITransactionFactory } from "@clairejs/orm";
2
3
  import { CustomJobInfo, JobInfo, JobInfoMetadata, ScheduledJob } from "./interfaces";
3
4
  interface JobHandlerMetadata extends JobInfoMetadata {
4
- handlerFn: (params?: any[]) => Promise<void>;
5
+ handlerFn: (job: ScheduledJob, tx: ITransaction) => Promise<ScheduledJob | undefined>;
5
6
  }
6
7
  export declare abstract class AbstractJobScheduler {
7
8
  protected readonly logger: AbstractLogger;
9
+ protected readonly db: ITransactionFactory;
8
10
  protected abstract isActiveScheduler(): boolean;
9
11
  private _jobs;
10
- constructor(logger: AbstractLogger);
12
+ constructor(logger: AbstractLogger, db: ITransactionFactory);
11
13
  protected getAvailableJobInfo(): Promise<JobHandlerMetadata[]>;
12
14
  abstract getAllScheduledJobs(): Promise<ScheduledJob[]>;
13
15
  /**
@@ -15,6 +17,7 @@ export declare abstract class AbstractJobScheduler {
15
17
  * @param jobInfo the necessary info to launch the job
16
18
  */
17
19
  protected abstract scheduleJob(jobInfo: JobInfo): Promise<string>;
20
+ protected abstract afterJob(job: ScheduledJob, tx: ITransaction): Promise<void>;
18
21
  /**
19
22
  * Sync all jobs to running state. This should be called only at init time.
20
23
  */
@@ -26,9 +29,9 @@ export declare abstract class AbstractJobScheduler {
26
29
  scheduleJobAt(jobInfo: CustomJobInfo): Promise<string>;
27
30
  /**
28
31
  * Remove the scheduled job and prevent if from running in the future
29
- * @param jobId The job id returned from scheduleJobAt function
32
+ * @param id The job id returned from scheduleJobAt function
30
33
  */
31
- abstract removeJob(jobId: string): Promise<void>;
34
+ abstract removeJob(id: string): Promise<void>;
32
35
  /**
33
36
  * Execute the scheduled job
34
37
  * @param job The schedled job info to execute
@@ -2,9 +2,11 @@ import { getServiceProvider } from "@clairejs/core";
2
2
  import { AbstractJobController } from "./AbstractJobController";
3
3
  export class AbstractJobScheduler {
4
4
  logger;
5
+ db;
5
6
  _jobs = null;
6
- constructor(logger) {
7
+ constructor(logger, db) {
7
8
  this.logger = logger;
9
+ this.db = db;
8
10
  }
9
11
  async getAvailableJobInfo() {
10
12
  if (this._jobs === null) {
@@ -33,15 +35,28 @@ export class AbstractJobScheduler {
33
35
  //-- run job
34
36
  const allJobs = await this.getAvailableJobInfo();
35
37
  const jobHandler = allJobs.find((j) => j.jobName === job.jobName);
36
- if (jobHandler) {
37
- await jobHandler.handlerFn(...(job.params || []));
38
+ if (!jobHandler) {
39
+ //-- remove job
40
+ this.logger.info(`Remove job with id: ${job.id} as handler is not found`);
41
+ await this.removeJob(job.id);
42
+ return;
38
43
  }
39
- if (!jobHandler || job.at) {
40
- //-- job not found so we remove it
41
- await this.removeJob(job.jobId);
42
- this.logger.info(!jobHandler
43
- ? `Remove not found job: ${job.jobName}`
44
- : `Remove one-time job ${job.jobName} at timestamp ${job.at}`);
44
+ const tx = await this.db.createTransaction();
45
+ try {
46
+ const newJob = await jobHandler.handlerFn({ ...job }, tx);
47
+ if (job.at) {
48
+ if (!newJob) {
49
+ await this.removeJob(job.id);
50
+ this.logger.info(`Remove one-time job ${job.jobName} at timestamp ${job.at}`);
51
+ }
52
+ else {
53
+ await this.afterJob(newJob, tx);
54
+ }
55
+ }
56
+ await tx.commit();
57
+ }
58
+ catch (err) {
59
+ await tx.rollback();
45
60
  }
46
61
  return;
47
62
  }
@@ -1,13 +1,16 @@
1
1
  import { AbstractLogger } from "@clairejs/core";
2
+ import { AbstractDatabaseAdapter, ITransaction } from "@clairejs/orm";
3
+ import aws from "aws-sdk";
2
4
  import Redis from "ioredis";
3
5
  import { AbstractJobScheduler } from "./AbstractJobScheduler";
4
6
  import { JobInfo, ScheduledJob } from "./interfaces";
5
7
  export declare class AwsJobScheduler extends AbstractJobScheduler {
6
- readonly logger: AbstractLogger;
7
- readonly redisClient: Redis;
8
- readonly uniqueIdKey: string;
9
- readonly apiLambdaFunctionArn: string;
10
- readonly stepFunctionName: string;
8
+ protected readonly logger: AbstractLogger;
9
+ protected readonly db: AbstractDatabaseAdapter;
10
+ protected readonly redisClient: Redis;
11
+ protected readonly uniqueIdKey: string;
12
+ protected readonly apiLambdaFunctionArn: string;
13
+ protected readonly stepFunctionName: string;
11
14
  /**
12
15
  * This IAM role must have following permissions:
13
16
  * - trigger any state machine (for one minute rule - as we don't now the state machine ARN until auto creation)
@@ -17,13 +20,13 @@ export declare class AwsJobScheduler extends AbstractJobScheduler {
17
20
  * - create event bridge rules, describe rules, remove rule, list and create rule targets
18
21
  * - create state machines, list state machines
19
22
  */
20
- readonly iamRoleArn: string;
21
- readonly eventBusName: string;
22
- readonly jobNamePrefix: string;
23
- readonly oneMinuteRule: string;
24
- private eventbridge;
25
- private stepfunctions;
26
- constructor(logger: AbstractLogger, redisClient: Redis, uniqueIdKey: string, apiLambdaFunctionArn: string, stepFunctionName: string,
23
+ protected readonly iamRoleArn: string;
24
+ protected readonly eventBusName: string;
25
+ protected readonly jobNamePrefix: string;
26
+ protected readonly oneMinuteRule: string;
27
+ protected readonly eventbridge: aws.EventBridge;
28
+ protected readonly stepfunctions: aws.StepFunctions;
29
+ constructor(logger: AbstractLogger, db: AbstractDatabaseAdapter, redisClient: Redis, uniqueIdKey: string, apiLambdaFunctionArn: string, stepFunctionName: string,
27
30
  /**
28
31
  * This IAM role must have following permissions:
29
32
  * - trigger any state machine (for one minute rule - as we don't now the state machine ARN until auto creation)
@@ -36,6 +39,7 @@ export declare class AwsJobScheduler extends AbstractJobScheduler {
36
39
  iamRoleArn: string, eventBusName?: string, jobNamePrefix?: string, oneMinuteRule?: string);
37
40
  handleInterval(interval: number): Promise<void>;
38
41
  handleCron(jobInfo: ScheduledJob): Promise<void>;
42
+ protected afterJob(_job: ScheduledJob, _tx: ITransaction): Promise<void>;
39
43
  private generateCronFromTimestamp;
40
44
  protected isActiveScheduler(): boolean;
41
45
  getAllScheduledJobs(): Promise<ScheduledJob[]>;
@@ -8,6 +8,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
8
8
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
9
  };
10
10
  import { AbstractLogger, Errors, LogContext } from "@clairejs/core";
11
+ import { AbstractDatabaseAdapter } from "@clairejs/orm";
11
12
  import aws from "aws-sdk";
12
13
  import Redis from "ioredis";
13
14
  import { AbstractJobScheduler } from "./AbstractJobScheduler";
@@ -104,6 +105,7 @@ const oneMinuteFunctionFactory = (lambdaFunctionARN, intervals) => {
104
105
  };
105
106
  let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
106
107
  logger;
108
+ db;
107
109
  redisClient;
108
110
  uniqueIdKey;
109
111
  apiLambdaFunctionArn;
@@ -114,7 +116,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
114
116
  oneMinuteRule;
115
117
  eventbridge = new aws.EventBridge();
116
118
  stepfunctions = new aws.StepFunctions({ apiVersion: "2016-11-23" });
117
- constructor(logger, redisClient, uniqueIdKey, apiLambdaFunctionArn, stepFunctionName,
119
+ constructor(logger, db, redisClient, uniqueIdKey, apiLambdaFunctionArn, stepFunctionName,
118
120
  /**
119
121
  * This IAM role must have following permissions:
120
122
  * - trigger any state machine (for one minute rule - as we don't now the state machine ARN until auto creation)
@@ -125,8 +127,9 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
125
127
  * - create state machines, list state machines
126
128
  */
127
129
  iamRoleArn, eventBusName = "default", jobNamePrefix = "claire-aws-job-", oneMinuteRule = "one-minute-step-function-trigger") {
128
- super(logger);
130
+ super(logger, db);
129
131
  this.logger = logger;
132
+ this.db = db;
130
133
  this.redisClient = redisClient;
131
134
  this.uniqueIdKey = uniqueIdKey;
132
135
  this.apiLambdaFunctionArn = apiLambdaFunctionArn;
@@ -140,12 +143,16 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
140
143
  this.logger.debug(`Handle interval`, interval, typeof interval);
141
144
  const allJobs = await this.getAvailableJobInfo();
142
145
  const timedJobs = allJobs.filter((job) => job.interval && interval % job.interval === 0);
143
- await Promise.all(timedJobs.map((job) => this.executeJob({ ...job, jobId: job.jobName })));
146
+ await Promise.all(timedJobs.map((job) => this.executeJob({ ...job, id: job.jobName })));
144
147
  }
145
148
  async handleCron(jobInfo) {
146
149
  this.logger.debug(`Handle cron`, jobInfo);
147
150
  await this.executeJob(jobInfo);
148
151
  }
152
+ async afterJob(_job, _tx) {
153
+ //-- do nothing
154
+ return;
155
+ }
149
156
  generateCronFromTimestamp(at) {
150
157
  const date = new Date(at);
151
158
  return [
@@ -186,7 +193,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
186
193
  //-- concat with interval jobs because we don't register them
187
194
  return allJobs
188
195
  .filter((job) => !!job)
189
- .concat(availableJobs.filter((j) => j.interval).map((j) => ({ ...j, jobId: j.jobName })));
196
+ .concat(availableJobs.filter((j) => j.interval).map((j) => ({ ...j, id: j.jobName })));
190
197
  }
191
198
  async scheduleJob(jobInfo) {
192
199
  this.logger.debug("Scheduling job: ", jobInfo);
@@ -245,8 +252,8 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
245
252
  //-- remove job that no more exist
246
253
  const nomoreExistJobs = scheduledJobs.filter((job) => !allJobs.find((j) => j.jobName === job.jobName));
247
254
  for (const job of nomoreExistJobs) {
248
- this.logger.info(`Removing stale job: ${job.jobName} of id: ${job.jobId}`);
249
- await this.removeJob(job.jobId);
255
+ this.logger.info(`Removing stale job: ${job.jobName} of id: ${job.id}`);
256
+ await this.removeJob(job.id);
250
257
  }
251
258
  if (nomoreExistJobs.length) {
252
259
  this.logger.info(`Cleaned up: ${nomoreExistJobs.length} stale jobs`);
@@ -254,7 +261,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
254
261
  //-- remove scheduled cron jobs
255
262
  this.logger.debug("Remove scheduled cron jobs");
256
263
  const scheduledCronJobs = scheduledJobs.filter((j) => j.cron);
257
- await Promise.all(scheduledCronJobs.map((j) => this.removeJob(j.jobId)));
264
+ await Promise.all(scheduledCronJobs.map((j) => this.removeJob(j.id)));
258
265
  //-- reschedule cron & interval jobs because we might have updated the cron expression / interval value
259
266
  const cronJobs = allJobs.filter((job) => job.cron || job.interval);
260
267
  this.logger.debug("Scheduling cron & interval jobs");
@@ -265,7 +272,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
265
272
  interval: job.interval,
266
273
  });
267
274
  }
268
- //-- keep at timestamp jobs as is
275
+ //-- keep "at" jobs as is
269
276
  }
270
277
  async removeJob(jobId) {
271
278
  await this.eventbridge
@@ -400,6 +407,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
400
407
  AwsJobScheduler = __decorate([
401
408
  LogContext(),
402
409
  __metadata("design:paramtypes", [AbstractLogger,
410
+ AbstractDatabaseAdapter,
403
411
  Redis, String, String, String, String, Object, Object, Object])
404
412
  ], AwsJobScheduler);
405
413
  export { AwsJobScheduler };
@@ -1,10 +1,12 @@
1
1
  import { AbstractLogger, IInit } from "@clairejs/core";
2
2
  import Redis from "ioredis";
3
+ import { AbstractDatabaseAdapter, ITransaction } from "@clairejs/orm";
3
4
  import { AbstractJobScheduler } from "./AbstractJobScheduler";
4
5
  import { JobInfo, ScheduledJob } from "./interfaces";
5
6
  import { AbstractJobRepository } from "./AbstractJobRepository";
6
7
  export declare class LocalJobScheduler extends AbstractJobScheduler implements IInit {
7
8
  protected readonly logger: AbstractLogger;
9
+ protected readonly db: AbstractDatabaseAdapter;
8
10
  protected readonly redisClient: Redis;
9
11
  protected readonly subscribeClient: Redis;
10
12
  protected readonly jobRepo: AbstractJobRepository;
@@ -33,7 +35,7 @@ export declare class LocalJobScheduler extends AbstractJobScheduler implements I
33
35
  private isActive;
34
36
  private notifyResolver;
35
37
  private jobHolder;
36
- constructor(logger: AbstractLogger, redisClient: Redis, subscribeClient: Redis, jobRepo: AbstractJobRepository,
38
+ constructor(logger: AbstractLogger, db: AbstractDatabaseAdapter, redisClient: Redis, subscribeClient: Redis, jobRepo: AbstractJobRepository,
37
39
  /**
38
40
  * Redis lock key to select active scheduler
39
41
  */
@@ -57,11 +59,12 @@ export declare class LocalJobScheduler extends AbstractJobScheduler implements I
57
59
  private sendJob;
58
60
  private processMessage;
59
61
  private extendMutexKey;
62
+ protected afterJob({ at, ...job }: ScheduledJob, tx: ITransaction): Promise<void>;
60
63
  init(): Promise<void>;
61
64
  exit(): void;
62
65
  protected isActiveScheduler(): boolean;
63
66
  getAllScheduledJobs(): Promise<ScheduledJob[]>;
64
67
  syncJobs(): Promise<void>;
65
68
  protected scheduleJob(jobInfo: JobInfo): Promise<string>;
66
- removeJob(jobId: string): Promise<void>;
69
+ removeJob(id: string): Promise<void>;
67
70
  }
@@ -11,9 +11,10 @@ import { AbstractLogger, Errors, Initable, LogContext } from "@clairejs/core";
11
11
  import Redis from "ioredis";
12
12
  import Redlock from "redlock";
13
13
  import scheduler from "node-schedule";
14
+ import { AbstractDatabaseAdapter } from "@clairejs/orm";
15
+ import assert from "assert";
14
16
  import { AbstractJobScheduler } from "./AbstractJobScheduler";
15
17
  import { AbstractJobRepository } from "./AbstractJobRepository";
16
- import { clearInterval, clearTimeout } from "timers";
17
18
  var CommunicationMessage;
18
19
  (function (CommunicationMessage) {
19
20
  CommunicationMessage["SCHEDULE_JOB"] = "SCHEDULE_JOB";
@@ -23,6 +24,7 @@ var CommunicationMessage;
23
24
  })(CommunicationMessage || (CommunicationMessage = {}));
24
25
  let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
25
26
  logger;
27
+ db;
26
28
  redisClient;
27
29
  subscribeClient;
28
30
  jobRepo;
@@ -36,7 +38,7 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
36
38
  isActive = false;
37
39
  notifyResolver = {};
38
40
  jobHolder = {};
39
- constructor(logger, redisClient, subscribeClient, jobRepo,
41
+ constructor(logger, db, redisClient, subscribeClient, jobRepo,
40
42
  /**
41
43
  * Redis lock key to select active scheduler
42
44
  */
@@ -57,8 +59,9 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
57
59
  * The time to lock active scheduler
58
60
  */
59
61
  keyRetentionDurationSecond = 30) {
60
- super(logger);
62
+ super(logger, db);
61
63
  this.logger = logger;
64
+ this.db = db;
62
65
  this.redisClient = redisClient;
63
66
  this.subscribeClient = subscribeClient;
64
67
  this.jobRepo = jobRepo;
@@ -116,6 +119,10 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
116
119
  await this.redisClient.setex(this.holdMutexKey, this.keyRetentionDurationSecond, 1);
117
120
  this.logger.debug("Scheduler extends mutex key");
118
121
  }
122
+ async afterJob({ at, ...job }, tx) {
123
+ assert.ok(at);
124
+ await this.jobRepo.saveJob({ ...job, at }, tx);
125
+ }
119
126
  async init() {
120
127
  this.logger.debug("LocalJobScheduler init");
121
128
  //-- subscribe to multi client channel
@@ -173,7 +180,7 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
173
180
  }
174
181
  }
175
182
  //-- re-schedule jobs that are stored in repo
176
- const allPersistedJobs = await this.jobRepo.getJobs();
183
+ const allPersistedJobs = await this.jobRepo.getJobs(this.db);
177
184
  //-- run job anyway, expired job will be removed then
178
185
  for (const job of allPersistedJobs) {
179
186
  await this.scheduleJob(job);
@@ -193,38 +200,39 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
193
200
  //-- case each job type
194
201
  if (jobInfo.at) {
195
202
  //-- create new schedule job
196
- const jobId = await this.jobRepo.saveJob({
197
- jobName: jobInfo.jobName,
198
- params: jobInfo.params,
199
- at: jobInfo.at,
200
- });
203
+ const id = jobInfo.id ||
204
+ (await this.jobRepo.saveJob({
205
+ jobName: jobInfo.jobName,
206
+ params: jobInfo.params,
207
+ at: jobInfo.at,
208
+ }, this.db));
201
209
  //-- use the lib
202
- const scheduledJob = { ...jobInfo, jobId };
210
+ const scheduledJob = { ...jobInfo, id };
203
211
  const timeout = setTimeout(() => {
204
- this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.jobId}`, err));
212
+ this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.id}`, err));
205
213
  }, new Date(jobInfo.at).getTime() - Date.now());
206
- this.jobHolder[jobId] = { jobCanceler: () => clearTimeout(timeout), jobInfo: { ...jobInfo, jobId } };
207
- return jobId;
214
+ this.jobHolder[id] = { jobCanceler: () => clearTimeout(timeout), jobInfo: { ...jobInfo, id } };
215
+ return id;
208
216
  }
209
217
  else if (jobInfo.interval) {
210
- const jobId = jobInfo.jobName;
218
+ const id = jobInfo.jobName;
211
219
  //-- set interval and does not need to persist
212
- const scheduledJob = { ...jobInfo, jobId };
220
+ const scheduledJob = { ...jobInfo, id };
213
221
  const interval = setInterval(() => {
214
- this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.jobId}`, err));
222
+ this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.id}`, err));
215
223
  }, jobInfo.interval);
216
- this.jobHolder[jobId] = { jobCanceler: () => clearInterval(interval), jobInfo: { ...jobInfo, jobId } };
217
- return jobId;
224
+ this.jobHolder[id] = { jobCanceler: () => clearInterval(interval), jobInfo: { ...jobInfo, id } };
225
+ return id;
218
226
  }
219
227
  else if (jobInfo.cron) {
220
- const jobId = jobInfo.jobName;
228
+ const id = jobInfo.jobName;
221
229
  //-- set cron and does not need to persist
222
- const scheduledJob = { ...jobInfo, jobId };
230
+ const scheduledJob = { ...jobInfo, id };
223
231
  const job = scheduler.scheduleJob(jobInfo.cron, () => {
224
- this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.jobId}`, err));
232
+ this.executeJob(scheduledJob).catch((err) => this.logger.error(`Error execute job ${scheduledJob.jobName} with id: ${scheduledJob.id}`, err));
225
233
  });
226
- this.jobHolder[jobId] = { jobCanceler: () => job.cancel(), jobInfo: { ...jobInfo, jobId } };
227
- return jobId;
234
+ this.jobHolder[id] = { jobCanceler: () => job.cancel(), jobInfo: { ...jobInfo, id } };
235
+ return id;
228
236
  }
229
237
  else {
230
238
  throw Errors.SYSTEM_ERROR(`Job does not have time config: ${jobInfo.jobName}`);
@@ -239,24 +247,23 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
239
247
  });
240
248
  }
241
249
  }
242
- async removeJob(jobId) {
250
+ async removeJob(id) {
243
251
  if (this.isActive) {
244
252
  //-- remove from holder
245
- const job = this.jobHolder[jobId];
253
+ const job = this.jobHolder[id];
246
254
  if (job) {
247
255
  job.jobCanceler();
248
- this.jobHolder[jobId] = undefined;
256
+ this.jobHolder[id] = undefined;
249
257
  }
250
258
  //-- remove from persistence
251
- await this.jobRepo.removeJobById(jobId);
252
- return;
259
+ await this.jobRepo.removeJobById(id, this.db);
253
260
  }
254
261
  else {
255
262
  //-- get unique message id
256
263
  const uniqueMessageId = await this.redisClient.incr(this.uniqueIdKey);
257
264
  return new Promise((resolve) => {
258
265
  this.notifyResolver[uniqueMessageId] = resolve;
259
- this.sendJob(CommunicationMessage.REMOVE_JOB, uniqueMessageId, jobId);
266
+ this.sendJob(CommunicationMessage.REMOVE_JOB, uniqueMessageId, id);
260
267
  });
261
268
  }
262
269
  }
@@ -265,6 +272,7 @@ LocalJobScheduler = __decorate([
265
272
  LogContext(),
266
273
  Initable(),
267
274
  __metadata("design:paramtypes", [AbstractLogger,
275
+ AbstractDatabaseAdapter,
268
276
  Redis,
269
277
  Redis,
270
278
  AbstractJobRepository, String, String, String, String, Number])
@@ -5,9 +5,8 @@ import { AbstractJobRepository } from "./AbstractJobRepository";
5
5
  import { AbstractJob } from "./job";
6
6
  export declare class DefaultJobRepository extends AbstractJobRepository {
7
7
  protected readonly model: Constructor<AbstractJob>;
8
- protected readonly db: IQueryProvider;
9
- constructor(model: Constructor<AbstractJob>, db: IQueryProvider);
10
- getJobs(): Promise<CustomJobInfo[]>;
11
- saveJob(jobInfo: CustomJobInfo): Promise<string>;
12
- removeJobById(jobId: string): Promise<void>;
8
+ constructor(model: Constructor<AbstractJob>);
9
+ getJobs(query: IQueryProvider): Promise<CustomJobInfo[]>;
10
+ saveJob({ id, ...jobInfo }: CustomJobInfo, query: IQueryProvider): Promise<string>;
11
+ removeJobById(jobId: string, query: IQueryProvider): Promise<void>;
13
12
  }
@@ -2,21 +2,27 @@ import { pickData } from "@clairejs/core";
2
2
  import { AbstractJobRepository } from "./AbstractJobRepository";
3
3
  export class DefaultJobRepository extends AbstractJobRepository {
4
4
  model;
5
- db;
6
- constructor(model, db) {
5
+ constructor(model) {
7
6
  super();
8
7
  this.model = model;
9
- this.db = db;
10
8
  }
11
- async getJobs() {
12
- const jobs = await this.db.use(this.model).getRecords();
13
- return jobs.map((job) => pickData(job, ["jobName", "at", "params"]));
9
+ async getJobs(query) {
10
+ const jobs = await query.use(this.model).getRecords();
11
+ return jobs.map((job) => pickData(job, ["jobName", "at", "params", "id"]));
14
12
  }
15
- async saveJob(jobInfo) {
16
- const job = await this.db.use(this.model).createOne(jobInfo);
17
- return job.id;
13
+ async saveJob({ id, ...jobInfo }, query) {
14
+ if (id) {
15
+ //-- update
16
+ await query.use(this.model).updateById(id, jobInfo);
17
+ }
18
+ else {
19
+ //-- create new
20
+ const job = await query.use(this.model).createOne(jobInfo);
21
+ id = job.id;
22
+ }
23
+ return id;
18
24
  }
19
- async removeJobById(jobId) {
20
- await this.db.use(this.model).deleteById(jobId);
25
+ async removeJobById(jobId, query) {
26
+ await query.use(this.model).deleteById(jobId);
21
27
  }
22
28
  }
@@ -34,11 +34,13 @@ export interface JobControllerMetadata extends ObjectMetadata {
34
34
  jobs?: JobInfoMetadata[];
35
35
  }
36
36
  export interface CustomJobInfo {
37
+ id?: string;
37
38
  jobName: string;
38
39
  at: number;
39
40
  params?: any[];
40
41
  }
41
42
  export interface JobInfo {
43
+ id?: string;
42
44
  jobName: string;
43
45
  params?: any[];
44
46
  at?: number;
@@ -46,5 +48,5 @@ export interface JobInfo {
46
48
  cron?: string;
47
49
  }
48
50
  export interface ScheduledJob extends JobInfo {
49
- jobId: string;
51
+ id: string;
50
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clairejs/server",
3
- "version": "3.16.13",
3
+ "version": "3.16.15",
4
4
  "description": "Claire server NodeJs framework written in Typescript.",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",