@chevre/domain 25.2.0-alpha.47 → 25.2.0-alpha.48

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.
@@ -1,45 +1,46 @@
1
1
  import type { Connection, UpdateWriteOpResult } from 'mongoose';
2
2
  import { factory } from '../factory';
3
- import { IModel } from './mongoose/schemas/task';
3
+ import { IModel } from './mongoose/schemas/asyncAction';
4
4
  import type { IExecutableTask } from '../taskSettings';
5
+ type ISavingTask = Pick<factory.task.IAttributes<factory.taskName>, 'alternateName' | 'data' | 'description' | 'executionResults' | 'expires' | 'identifier' | 'name' | 'project' | 'runsAt' | 'status'> & {
6
+ expires: Date;
7
+ };
8
+ export type IExecutableAsyncAction = Pick<IExecutableTask<factory.taskName>, 'data' | 'expires' | 'id' | 'name' | 'project' | 'runsAt' | 'status'>;
5
9
  /**
6
- * タスクリポジトリ
10
+ * 非同期アクションリポジトリ
7
11
  */
8
12
  export declare class AsyncActionRepo {
9
- readonly taskModel: IModel;
13
+ readonly asyncActionModel: IModel;
10
14
  constructor(connection: Connection);
11
15
  /**
12
16
  * Readyタスクをひとつ追加する
13
17
  */
14
- addReadyAsyncAction(params: Pick<factory.task.IAttributes<factory.taskName>, 'data' | 'expires' | 'name' | 'project' | 'remainingNumberOfTries' | 'runsAt'> & {
18
+ addReadyAsyncAction(params: Pick<ISavingTask, 'data' | 'expires' | 'name' | 'project' | 'runsAt'> & {
15
19
  alternateName?: never;
16
20
  identifier?: never;
17
- expires: Date;
18
21
  }): Promise<{
19
22
  id: string;
20
23
  }>;
21
24
  /**
22
- * Ready -> remainingNumberOfTriesが1減る
25
+ * Ready -> Running
23
26
  */
24
27
  executeAsyncActionById(params: {
25
28
  id: string;
26
- status: factory.taskStatus.Ready;
27
29
  executor: {
28
30
  name: string;
29
31
  };
30
- expires?: Date;
31
- }): Promise<IExecutableTask<factory.taskName> | null>;
32
+ }): Promise<IExecutableAsyncAction | null>;
32
33
  /**
33
34
  * Readyのままで期限切れのタスクをExpiredに変更する
34
35
  */
35
36
  makeExpiredMany(params: {
36
37
  expiresLt: Date;
37
38
  }): Promise<UpdateWriteOpResult>;
39
+ /**
40
+ * taskNameに関わらず、Runningのまま放置された非同期アクションをAbortedに変更する
41
+ */
38
42
  abortMany(params: {
39
43
  intervalInMinutes: number;
40
- name: {
41
- $in?: factory.taskName[];
42
- };
43
44
  }): Promise<UpdateWriteOpResult>;
44
45
  /**
45
46
  * タスクIDから実行結果とステータスを保管する
@@ -63,3 +64,4 @@ export declare class AsyncActionRepo {
63
64
  };
64
65
  }): Promise<import("mongodb").DeleteResult>;
65
66
  }
67
+ export {};
@@ -7,7 +7,7 @@ exports.AsyncActionRepo = void 0;
7
7
  const moment_1 = __importDefault(require("moment"));
8
8
  const factory_1 = require("../factory");
9
9
  const settings_1 = require("../settings");
10
- const task_1 = require("./mongoose/schemas/task");
10
+ const asyncAction_1 = require("./mongoose/schemas/asyncAction");
11
11
  const executableTaskProjection = {
12
12
  _id: 0,
13
13
  id: { $toString: '$_id' },
@@ -15,38 +15,32 @@ const executableTaskProjection = {
15
15
  data: 1,
16
16
  name: 1,
17
17
  status: 1,
18
- numberOfTried: 1,
19
18
  project: 1,
20
- remainingNumberOfTries: 1,
21
19
  runsAt: 1,
22
20
  expires: 1
23
21
  };
24
22
  /**
25
- * タスクリポジトリ
23
+ * 非同期アクションリポジトリ
26
24
  */
27
25
  class AsyncActionRepo {
28
- taskModel;
26
+ asyncActionModel;
29
27
  constructor(connection) {
30
- this.taskModel = connection.model(task_1.modelName, (0, task_1.createSchema)());
28
+ this.asyncActionModel = connection.model(asyncAction_1.modelName, (0, asyncAction_1.createSchema)());
31
29
  }
32
30
  /**
33
31
  * Readyタスクをひとつ追加する
34
32
  */
35
- async addReadyAsyncAction(
36
- // resolve uniqueness of identifier(2025-03-27~)
37
- params) {
33
+ async addReadyAsyncAction(params) {
38
34
  const { expires } = params;
39
35
  if (!(expires instanceof Date)) {
40
36
  throw new factory_1.factory.errors.Argument('expires', 'must be Date');
41
37
  }
42
38
  const savingTask = {
43
39
  ...params,
44
- remainingNumberOfTries: 1, // リトライはありえないので、1で固定
45
40
  status: factory_1.factory.taskStatus.Ready,
46
- numberOfTried: 0,
47
41
  executionResults: []
48
42
  };
49
- const result = await this.taskModel.insertMany(savingTask, { rawResult: true });
43
+ const result = await this.asyncActionModel.insertMany(savingTask, { rawResult: true });
50
44
  const id = result.insertedIds?.[0]?.toHexString();
51
45
  if (typeof id !== 'string') {
52
46
  throw new factory_1.factory.errors.Internal('task not saved');
@@ -54,25 +48,21 @@ class AsyncActionRepo {
54
48
  return { id };
55
49
  }
56
50
  /**
57
- * Ready -> remainingNumberOfTriesが1減る
51
+ * Ready -> Running
58
52
  */
59
53
  async executeAsyncActionById(params) {
60
54
  const now = new Date();
61
- const doc = await this.taskModel.findOneAndUpdate({
55
+ const doc = await this.asyncActionModel.findOneAndUpdate({
62
56
  _id: { $eq: params.id },
63
- status: { $eq: params.status },
57
+ status: { $eq: factory_1.factory.taskStatus.Ready }, // Readyのものしか実行しない
64
58
  runsAt: { $lt: now },
65
- ...(params.expires instanceof Date) ? { expires: { $gt: now } } : undefined
59
+ expires: { $exists: true, $gt: now }, // ワンタイムタスクなのでexpiresは必ず存在する
66
60
  }, {
67
61
  $set: {
68
62
  status: factory_1.factory.taskStatus.Running, // 実行中に変更
69
63
  lastTriedAt: now,
70
64
  executor: params.executor
71
65
  },
72
- $inc: {
73
- remainingNumberOfTries: -1, // 残りトライ可能回数減らす
74
- numberOfTried: 1 // トライ回数増やす
75
- }
76
66
  }, { new: true, projection: executableTaskProjection })
77
67
  .setOptions({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
78
68
  .lean() // lean(2024-09-26~)
@@ -90,7 +80,7 @@ class AsyncActionRepo {
90
80
  if (!(expiresLt instanceof Date)) {
91
81
  throw new factory_1.factory.errors.Argument('expiresLt', 'must be Date');
92
82
  }
93
- return this.taskModel.updateMany({
83
+ return this.asyncActionModel.updateMany({
94
84
  status: { $eq: factory_1.factory.taskStatus.Ready },
95
85
  expires: { $exists: true, $lt: expiresLt }
96
86
  }, {
@@ -100,18 +90,19 @@ class AsyncActionRepo {
100
90
  })
101
91
  .exec();
102
92
  }
93
+ /**
94
+ * taskNameに関わらず、Runningのまま放置された非同期アクションをAbortedに変更する
95
+ */
103
96
  async abortMany(params) {
104
97
  const lastTriedAtShoudBeLessThan = (0, moment_1.default)()
105
98
  .add(-params.intervalInMinutes, 'minutes')
106
99
  .toDate();
107
- return this.taskModel.updateMany({
100
+ return this.asyncActionModel.updateMany({
108
101
  status: { $eq: factory_1.factory.taskStatus.Running },
109
102
  lastTriedAt: {
110
- $type: 'date',
103
+ $exists: true,
111
104
  $lt: lastTriedAtShoudBeLessThan
112
- },
113
- remainingNumberOfTries: { $eq: 0 },
114
- ...(Array.isArray(params.name.$in)) ? { name: { $in: params.name.$in } } : undefined
105
+ }
115
106
  }, {
116
107
  $set: {
117
108
  status: factory_1.factory.taskStatus.Aborted,
@@ -127,7 +118,7 @@ class AsyncActionRepo {
127
118
  async setExecutionResultAndStatus(params, update) {
128
119
  const { id } = params;
129
120
  const { status, executionResult } = update;
130
- await this.taskModel.updateOne({ _id: { $eq: id } }, {
121
+ await this.asyncActionModel.updateOne({ _id: { $eq: id } }, {
131
122
  $set: {
132
123
  status,
133
124
  ...(status === factory_1.factory.taskStatus.Aborted) ? { dateAborted: executionResult.endDate } : undefined // 2025-08-04~
@@ -140,7 +131,7 @@ class AsyncActionRepo {
140
131
  * 不要なタスクを削除する
141
132
  */
142
133
  async deleteRunsAtPassedCertainPeriod(params) {
143
- return this.taskModel.deleteMany({
134
+ return this.asyncActionModel.deleteMany({
144
135
  runsAt: { $lt: params.runsAt.$lt },
145
136
  status: {
146
137
  $in: [
@@ -0,0 +1,11 @@
1
+ import { IndexDefinition, IndexOptions, Model, Schema, SchemaDefinition } from 'mongoose';
2
+ import { IVirtuals } from '../virtuals';
3
+ import { factory } from '../../../factory';
4
+ type IDocType = Pick<factory.task.ITask<factory.taskName>, 'alternateName' | 'data' | 'dateAborted' | 'description' | 'executionResults' | 'executor' | 'expires' | 'identifier' | 'lastTriedAt' | 'name' | 'project' | 'runsAt' | 'status'>;
5
+ type IModel = Model<IDocType, Record<string, never>, Record<string, never>, IVirtuals>;
6
+ type ISchemaDefinition = SchemaDefinition<IDocType>;
7
+ type ISchema = Schema<IDocType, IModel, Record<string, never>, Record<string, never>, IVirtuals, Record<string, never>, ISchemaDefinition, IDocType>;
8
+ declare const modelName = "AsyncAction";
9
+ declare const indexes: [d: IndexDefinition, o: IndexOptions][];
10
+ declare function createSchema(): ISchema;
11
+ export { createSchema, IDocType, IModel, indexes, modelName };
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modelName = exports.indexes = void 0;
4
+ exports.createSchema = createSchema;
5
+ const mongoose_1 = require("mongoose");
6
+ const writeConcern_1 = require("../writeConcern");
7
+ const settings_1 = require("../../../settings");
8
+ const modelName = 'AsyncAction';
9
+ exports.modelName = modelName;
10
+ const schemaDefinition = {
11
+ alternateName: String, // add(2025-03-28~)
12
+ identifier: String,
13
+ description: String, // add(2025-03-30~)
14
+ project: { type: mongoose_1.SchemaTypes.Mixed, required: true },
15
+ name: { type: String, required: true },
16
+ status: { type: String, required: true },
17
+ runsAt: { type: Date, required: true },
18
+ expires: { type: Date, required: true },
19
+ lastTriedAt: Date,
20
+ executionResults: [mongoose_1.SchemaTypes.Mixed],
21
+ executor: mongoose_1.SchemaTypes.Mixed,
22
+ data: mongoose_1.SchemaTypes.Mixed,
23
+ dateAborted: Date,
24
+ // remainingNumberOfTries: { type: Number, required: true }, // ワンタイム前提につき不要
25
+ // numberOfTried: { type: Number, required: true }, // ワンタイム前提につき不要
26
+ };
27
+ const schemaOptions = {
28
+ autoIndex: settings_1.MONGO_AUTO_INDEX,
29
+ autoCreate: false,
30
+ collection: 'asyncActions',
31
+ id: true,
32
+ read: 'primary',
33
+ writeConcern: writeConcern_1.writeConcern,
34
+ strict: true,
35
+ strictQuery: false,
36
+ timestamps: false,
37
+ versionKey: false,
38
+ toJSON: {
39
+ getters: false,
40
+ virtuals: false,
41
+ minimize: false,
42
+ versionKey: false
43
+ },
44
+ toObject: {
45
+ getters: false,
46
+ virtuals: true,
47
+ minimize: false,
48
+ versionKey: false
49
+ }
50
+ };
51
+ const indexes = [
52
+ [
53
+ { 'project.id': 1, runsAt: -1 },
54
+ { name: 'projectId' }
55
+ ],
56
+ [
57
+ { name: 1, runsAt: -1 },
58
+ { name: 'asyncActionName' }
59
+ ],
60
+ [
61
+ { status: 1, runsAt: -1 },
62
+ { name: 'status' }
63
+ ],
64
+ [
65
+ { runsAt: -1 },
66
+ { name: 'runsAt' }
67
+ ],
68
+ [
69
+ { identifier: 1, runsAt: -1 },
70
+ {
71
+ name: 'identifier',
72
+ partialFilterExpression: {
73
+ identifier: { $exists: true }
74
+ }
75
+ }
76
+ ],
77
+ [
78
+ { dateAborted: 1, runsAt: -1 },
79
+ {
80
+ name: 'dateAborted',
81
+ partialFilterExpression: {
82
+ dateAborted: { $exists: true }
83
+ }
84
+ }
85
+ ],
86
+ [
87
+ { lastTriedAt: 1, runsAt: -1 },
88
+ {
89
+ name: 'lastTriedAt',
90
+ partialFilterExpression: {
91
+ lastTriedAt: { $exists: true }
92
+ }
93
+ }
94
+ ],
95
+ [
96
+ { expires: 1, runsAt: -1 },
97
+ {
98
+ name: 'expires',
99
+ partialFilterExpression: {
100
+ expires: { $exists: true }
101
+ }
102
+ }
103
+ ],
104
+ [
105
+ { status: 1, lastTriedAt: 1 },
106
+ {
107
+ name: 'abortMany',
108
+ partialFilterExpression: {
109
+ lastTriedAt: { $exists: true }
110
+ }
111
+ }
112
+ ],
113
+ [
114
+ { status: 1, expires: 1 },
115
+ {
116
+ name: 'makeExpiredMany',
117
+ partialFilterExpression: {
118
+ expires: { $exists: true }
119
+ }
120
+ }
121
+ ],
122
+ [
123
+ // add unique index(2025-03-28~)
124
+ { alternateName: 1 },
125
+ {
126
+ unique: true,
127
+ name: 'uniqueAlternateName',
128
+ partialFilterExpression: {
129
+ alternateName: { $exists: true }
130
+ }
131
+ }
132
+ ],
133
+ [
134
+ { identifier: 1 },
135
+ {
136
+ unique: true,
137
+ name: 'uniqueIdentifier',
138
+ partialFilterExpression: {
139
+ identifier: { $exists: true }
140
+ }
141
+ }
142
+ ]
143
+ ];
144
+ exports.indexes = indexes;
145
+ /**
146
+ * 非同期アクションスキーマ
147
+ */
148
+ let schema;
149
+ function createSchema() {
150
+ if (schema === undefined) {
151
+ schema = new mongoose_1.Schema(schemaDefinition, schemaOptions);
152
+ if (settings_1.MONGO_AUTO_INDEX) {
153
+ indexes.forEach((indexParams) => {
154
+ schema?.index(...indexParams);
155
+ });
156
+ }
157
+ }
158
+ return schema;
159
+ }
@@ -1,7 +1,11 @@
1
- import type { IExecuteSettings as IMinimumExecuteSettings, INextFunction, IReadyTask } from '../eventEmitter/task';
1
+ import type { IExecuteSettings as IMinimumExecuteSettings, INextFunction } from '../eventEmitter/task';
2
2
  type IExecuteSettings = IMinimumExecuteSettings;
3
3
  type IOperation<T> = (settings: IExecuteSettings) => Promise<T>;
4
- declare function executeAsyncActionById(params: IReadyTask & {
4
+ declare function executeAsyncActionById(params: {
5
+ /**
6
+ * タスクID
7
+ */
8
+ id: string;
5
9
  executor: {
6
10
  /**
7
11
  * タスク実行者名称
@@ -13,14 +13,12 @@ const debug = (0, debug_1.default)('chevre-domain:service:task');
13
13
  function executeAsyncActionById(params, next) {
14
14
  return async (settings) => {
15
15
  const asyncActionRepo = new (await import('../repo/asyncAction.js')).AsyncActionRepo(settings.connection);
16
- const { id, status, executor, expires } = params;
16
+ const { id, executor } = params;
17
17
  let task = null;
18
18
  try {
19
19
  task = await asyncActionRepo.executeAsyncActionById({
20
20
  id,
21
- status,
22
- executor: { name: executor.name },
23
- ...(expires instanceof Date) ? { expires } : undefined
21
+ executor: { name: executor.name }
24
22
  });
25
23
  }
26
24
  catch (error) {
@@ -1,13 +1,11 @@
1
1
  import type { IExecutedTask } from '../../eventEmitter/task';
2
- import { factory } from '../../factory';
3
2
  import type { SettingRepo } from '../../repo/setting';
4
- import type { AsyncActionRepo } from '../../repo/asyncAction';
5
- import type { IExecutableTask } from '../../taskSettings';
3
+ import type { AsyncActionRepo, IExecutableAsyncAction } from '../../repo/asyncAction';
6
4
  /**
7
5
  * タスク実行失敗時処理
8
6
  */
9
7
  declare function onOperationFailed(params: {
10
- task: IExecutableTask<factory.taskName>;
8
+ task: IExecutableAsyncAction;
11
9
  now: Date;
12
10
  error: any;
13
11
  }): (repos: {
@@ -32,7 +32,7 @@ function onOperationFailed(params) {
32
32
  });
33
33
  const executedTask = {
34
34
  id: task.id,
35
- remainingNumberOfTries: task.remainingNumberOfTries,
35
+ remainingNumberOfTries: 0,
36
36
  name: task.name,
37
37
  status: task.status,
38
38
  executionResult: result
@@ -1,6 +1,6 @@
1
1
  import type { INextFunction } from '../eventEmitter/task';
2
- import { factory } from '../factory';
3
- import type { ICallResult, ICallableTaskOperation, IExecutableTask, IExecutableTaskKeys, IOperationExecute } from '../taskSettings';
2
+ import type { ICallResult, ICallableTaskOperation, IExecutableTaskKeys, IOperationExecute } from '../taskSettings';
3
+ import type { IExecutableAsyncAction } from '../repo/asyncAction';
4
4
  export declare const taskLoader: {
5
5
  load: (taskName: string) => Promise<{
6
6
  call: ICallableTaskOperation;
@@ -9,5 +9,5 @@ export declare const taskLoader: {
9
9
  /**
10
10
  * 非同期アクションを実行する
11
11
  */
12
- declare function executeAsyncAction(task: IExecutableTask<factory.taskName>, next?: INextFunction): IOperationExecute<void>;
12
+ declare function executeAsyncAction(task: IExecutableAsyncAction, next?: INextFunction): IOperationExecute<void>;
13
13
  export { ICallableTaskOperation, ICallResult, IExecutableTaskKeys, IOperationExecute, executeAsyncAction };
@@ -34,16 +34,15 @@ function executeAsyncAction(task, next) {
34
34
  throw new factory_1.factory.errors.Internal(`task expired [expires:${task.expires}]`);
35
35
  }
36
36
  }
37
+ // remainingNumberOfTriesは存在しないので検証不要
37
38
  // remainingNumberOfTries<0ならcallを実行しない(2025-08-04~)
38
- // リトライを繰り返した後、不明の原因でRunningのまま残ってしまったタスクがリトライされたケース
39
- const { remainingNumberOfTries } = task;
40
- const isCallable = remainingNumberOfTries >= 0;
41
- if (!isCallable) {
42
- throw new factory_1.factory.errors.Internal(`task remainingNumberOfTries < 0 [remainingNumberOfTries:${remainingNumberOfTries}]`);
43
- }
44
39
  // タスク名の関数が定義されていなければ、TypeErrorとなる
45
40
  const { call } = await exports.taskLoader.load(task.name); // testが書きづらいのでtaskLoader経由で呼ぶ
46
- const callResult = await call(task)(settings, options);
41
+ const callResult = await call({
42
+ ...task,
43
+ numberOfTried: 1, // ひとまず固定でok(2026-07-26~)
44
+ remainingNumberOfTries: 0 // ひとまず固定でok(2026-07-26~)
45
+ })(settings, options);
47
46
  const result = {
48
47
  executedAt: now,
49
48
  endDate: new Date(),
package/package.json CHANGED
@@ -88,5 +88,5 @@
88
88
  "postversion": "git push origin --tags",
89
89
  "prepublishOnly": "npm run clean && npm run build"
90
90
  },
91
- "version": "25.2.0-alpha.47"
91
+ "version": "25.2.0-alpha.48"
92
92
  }