@natlibfi/melinda-rest-api-commons 6.0.1 → 6.2.0-alpha.0

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.
Files changed (94) hide show
  1. package/.github/workflows/melinda-node-tests-and-publish.yml +4 -2
  2. package/README.md +82 -0
  3. package/dist/amqp.d.ts +41 -0
  4. package/dist/amqp.d.ts.map +1 -0
  5. package/dist/amqp.js +242 -218
  6. package/dist/amqp.js.map +1 -7
  7. package/dist/constants.d.ts +49 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/constants.js +38 -38
  10. package/dist/constants.js.map +1 -7
  11. package/dist/helpers/conversion.d.ts +8 -0
  12. package/dist/helpers/conversion.d.ts.map +1 -0
  13. package/dist/helpers/conversion.js +70 -65
  14. package/dist/helpers/conversion.js.map +1 -7
  15. package/dist/helpers/fix-constants.d.ts +76 -0
  16. package/dist/helpers/fix-constants.d.ts.map +1 -0
  17. package/dist/helpers/fix-constants.js +41 -41
  18. package/dist/helpers/fix-constants.js.map +1 -7
  19. package/dist/helpers/fix-handle-tempurns.d.ts +3 -0
  20. package/dist/helpers/fix-handle-tempurns.d.ts.map +1 -0
  21. package/dist/helpers/fix-handle-tempurns.js +73 -71
  22. package/dist/helpers/fix-handle-tempurns.js.map +1 -7
  23. package/dist/helpers/fix-strip-f884s.d.ts +10 -0
  24. package/dist/helpers/fix-strip-f884s.d.ts.map +1 -0
  25. package/dist/helpers/fix-strip-f884s.js +163 -151
  26. package/dist/helpers/fix-strip-f884s.js.map +1 -7
  27. package/dist/helpers/fix.d.ts +6 -0
  28. package/dist/helpers/fix.d.ts.map +1 -0
  29. package/dist/helpers/fix.js +133 -117
  30. package/dist/helpers/fix.js.map +1 -7
  31. package/dist/helpers/validation.d.ts +9 -0
  32. package/dist/helpers/validation.d.ts.map +1 -0
  33. package/dist/helpers/validation.js +14 -16
  34. package/dist/helpers/validation.js.map +1 -7
  35. package/dist/index.d.ts +13 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +1 -1
  38. package/dist/index.js.map +1 -7
  39. package/dist/mongo.d.ts +165 -0
  40. package/dist/mongo.d.ts.map +1 -0
  41. package/dist/mongo.js +603 -464
  42. package/dist/mongo.js.map +1 -7
  43. package/dist/mongoError.d.ts +2 -0
  44. package/dist/mongoError.d.ts.map +1 -0
  45. package/dist/mongoError.js +19 -0
  46. package/dist/mongoError.js.map +1 -0
  47. package/dist/mongoLog.d.ts +50 -0
  48. package/dist/mongoLog.d.ts.map +1 -0
  49. package/dist/mongoLog.js +282 -172
  50. package/dist/mongoLog.js.map +1 -7
  51. package/dist/utils.d.ts +36 -0
  52. package/dist/utils.d.ts.map +1 -0
  53. package/dist/utils.js +118 -87
  54. package/dist/utils.js.map +1 -7
  55. package/eslint.config.ts +66 -0
  56. package/package.json +23 -25
  57. package/test/amqp-error-branches.test.ts +551 -0
  58. package/{tests/amqp.healthtest.js → test/amqp.healthtest.ts} +19 -7
  59. package/{tests/amqp.tests.localtest.js → test/amqp.tests.localtest.ts} +66 -52
  60. package/{tests/helpers/conversion.test.js → test/helpers/conversion.test.ts} +39 -11
  61. package/test/helpers/fix-edge.test.ts +201 -0
  62. package/test/helpers/fix.test.ts +65 -0
  63. package/{tests/helpers/validation.test.js → test/helpers/validation.test.ts} +13 -8
  64. package/{tests/mongo-add-blob-size.test.js → test/mongo-add-blob-size.test.ts} +14 -15
  65. package/{tests/mongo-check-and-check-state.test.js → test/mongo-check-and-check-state.test.ts} +9 -9
  66. package/{tests/mongo-check-and-set-import-job-state.test.js → test/mongo-check-and-set-import-job-state.test.ts} +9 -10
  67. package/{tests/mongo-check-timeout.test.js → test/mongo-check-timeout.test.ts} +6 -9
  68. package/{tests/mongo-create-bulk.test.js → test/mongo-create-bulk.test.ts} +6 -7
  69. package/{tests/mongo-create-prio.test.js → test/mongo-create-prio.test.ts} +6 -6
  70. package/test/mongo-error-branches.test.ts +883 -0
  71. package/{tests/mongo-get-one.test.js → test/mongo-get-one.test.ts} +6 -6
  72. package/{tests/mongo-get-stream.test.js → test/mongo-get-stream.test.ts} +10 -9
  73. package/{tests/mongo-push-ids.test.js → test/mongo-push-ids.test.ts} +6 -6
  74. package/{tests/mongo-push-messages.test.js → test/mongo-push-messages.test.ts} +6 -6
  75. package/{tests/mongo-query-by-id.test.js → test/mongo-query-by-id.test.ts} +6 -9
  76. package/{tests/mongo-query.test.js → test/mongo-query.test.ts} +6 -7
  77. package/{tests/mongo-read-content.test.js → test/mongo-read-content.test.ts} +16 -9
  78. package/{tests/mongo-remove.test.js → test/mongo-remove.test.ts} +6 -8
  79. package/{tests/mongo-set-blob-size.test.js → test/mongo-set-blob-size.test.ts} +6 -6
  80. package/{tests/mongo-set-import-job-state.test.js → test/mongo-set-import-job-state.test.ts} +6 -6
  81. package/{tests/mongo-set-operation.test.js → test/mongo-set-operation.test.ts} +6 -6
  82. package/{tests/mongo-set-operations.test.js → test/mongo-set-operations.test.ts} +6 -6
  83. package/{tests/mongo-set-state.test.js → test/mongo-set-state.test.ts} +6 -6
  84. package/test/mongoLog-protect.test.ts +182 -0
  85. package/test/mongoLog.test.ts +191 -0
  86. package/{tests/testUtils.js → test/testUtils.ts} +49 -15
  87. package/test/utils.test.ts +360 -0
  88. package/tsconfig.json +62 -0
  89. package/tsconfig.test.json +17 -0
  90. package/dist/helpers/fix-utils.js +0 -39
  91. package/dist/helpers/fix-utils.js.map +0 -7
  92. package/eslint.config.js +0 -53
  93. package/tests/helpers/fix.test.js +0 -41
  94. package/tests/mongoLog.test.js +0 -84
package/dist/mongo.js CHANGED
@@ -1,486 +1,625 @@
1
- import { MongoClient, GridFSBucket, MongoDriverError } from "mongodb";
2
- import moment from "moment";
3
- import httpStatus from "http-status";
4
- import sanitize from "mongo-sanitize";
5
- import createDebugLogger from "debug";
6
- import { createLogger } from "@natlibfi/melinda-backend-commons";
7
- import { Error as ApiError, parseBoolean } from "@natlibfi/melinda-commons";
1
+ import { MongoClient, GridFSBucket, MongoDriverError } from 'mongodb';
2
+ import httpStatus from 'http-status';
3
+ import createDebugLogger from 'debug';
4
+ import { createLogger, Error as ApiError, parseBoolean } from '@natlibfi/melinda-commons';
8
5
  import { QUEUE_ITEM_STATE, IMPORT_JOB_STATE, OPERATIONS } from "./constants.js";
9
- import { logError } from "./utils.js";
10
- export default async function(MONGO_URI, collection, db = "rest-api") {
11
- const logger = createLogger();
12
- const debug = createDebugLogger("@natlibfi/melinda-rest-api-commons/mongo");
13
- const debugDev = debug.extend(":dev");
14
- const client = await MongoClient.connect(MONGO_URI);
15
- const dbConnection = client.db(db);
16
- const gridFSBucket = new GridFSBucket(dbConnection, { bucketName: collection });
17
- const operator = dbConnection.collection(collection);
18
- debugDev(`mongo: ${MONGO_URI}, db: ${db}, collection: ${collection}`);
19
- return { createPrio, createBulk, checkAndSetState, checkAndSetImportJobState, query, createProjection, queryById, checkTimeOut, remove, readContent, removeContent, getOne, getStream, setState, setImportJobState, pushIds, pushMessages, setOperation, setOperations, addBlobSize, setBlobSize };
20
- async function createPrio({ correlationId, cataloger, oCatalogerIn, operation, operationSettings }) {
21
- debugDev("createPrio");
22
- const time = moment().toDate();
23
- const newQueueItem = {
24
- correlationId,
25
- cataloger,
26
- operation,
27
- operations: [operation],
28
- oCatalogerIn,
29
- operationSettings: {
30
- prio: true,
31
- ...operationSettings
32
- },
33
- queueItemState: QUEUE_ITEM_STATE.VALIDATOR.PENDING_VALIDATION,
34
- blobSize: 1,
35
- importJobState: {
36
- CREATE: IMPORT_JOB_STATE.EMPTY,
37
- UPDATE: IMPORT_JOB_STATE.EMPTY,
38
- FIX: IMPORT_JOB_STATE.EMPTY
39
- },
40
- records: [],
41
- errorMessage: "",
42
- errorStatus: "",
43
- creationTime: time,
44
- modificationTime: time
45
- };
46
- try {
47
- debugDev(`Inserting: ${JSON.stringify(newQueueItem)} to ${collection}}`);
48
- const result = await operator.insertOne(newQueueItem);
49
- debugDev(JSON.stringify(result));
50
- if (result.acknowledged) {
51
- logger.info(`New PRIO queue item for ${operation} ${correlationId} has been made in ${collection}`);
52
- return;
53
- }
54
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR);
55
- } catch (error) {
56
- const errorMessage = error.payload || error.message || "";
57
- logError(error);
58
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
6
+ import { logError, assertString, assertNumber, assertImportJobStateFilter } from "./utils.js";
7
+ import { withMongoError } from "./mongoError.js";
8
+ export default async function (MONGO_URI, collection, db = 'rest-api', deps = {}) {
9
+ const logger = createLogger();
10
+ const timeoutMinutes = Number.parseInt(process.env['MONGO_TIMEOUT_MINUTES'], 10) || 1;
11
+ logger.debug(`checkTimeOut window: ${timeoutMinutes} minute(s) (override with MONGO_TIMEOUT_MINUTES)`);
12
+ const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons/mongo');
13
+ const debugDev = debug.extend(':dev');
14
+ const stubbed = Boolean(deps.operator) && Boolean(deps.gridFSBucket);
15
+ let gridFSBucket = deps.gridFSBucket;
16
+ let operator = deps.operator;
17
+ if (!stubbed) {
18
+ const client = await MongoClient.connect(MONGO_URI);
19
+ const realDb = client.db(db);
20
+ gridFSBucket = gridFSBucket ?? new GridFSBucket(realDb, { bucketName: collection });
21
+ operator = operator ?? realDb.collection(collection);
59
22
  }
60
- }
61
- async function createBulk({ correlationId, cataloger, oCatalogerIn, operation, contentType, recordLoadParams, stream, operationSettings }) {
62
- const time = moment().toDate();
63
- const newQueueItem = {
64
- correlationId,
65
- cataloger,
66
- oCatalogerIn,
67
- operation,
68
- operations: [operation],
69
- operationSettings: {
70
- prio: false,
71
- ...operationSettings
72
- },
73
- contentType,
74
- recordLoadParams,
75
- queueItemState: stream ? QUEUE_ITEM_STATE.VALIDATOR.UPLOADING : QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS,
76
- blobSize: 0,
77
- importJobState: {
78
- CREATE: IMPORT_JOB_STATE.EMPTY,
79
- UPDATE: IMPORT_JOB_STATE.EMPTY,
80
- FIX: IMPORT_JOB_STATE.EMPTY
81
- },
82
- records: [],
83
- errorMessage: "",
84
- errorStatus: "",
85
- creationTime: time,
86
- modificationTime: time
23
+ debugDev(`mongo: ${MONGO_URI}, db: ${db}, collection: ${collection}`);
24
+ return {
25
+ createPrio,
26
+ createBulk,
27
+ checkAndSetState: (params) => withMongoError(() => checkAndSetState(params)),
28
+ checkAndSetImportJobState: (params) => withMongoError(() => checkAndSetImportJobState(params)),
29
+ query: (params, showParams) => withMongoError(() => query(params, showParams)),
30
+ createProjection,
31
+ queryById: (params) => withMongoError(() => queryById(params)),
32
+ checkTimeOut: (params) => withMongoError(() => checkTimeOut(params)),
33
+ remove,
34
+ readContent: (correlationId) => withMongoError(() => readContent(correlationId)),
35
+ removeContent: (params) => withMongoError(() => removeContent(params)),
36
+ getOne,
37
+ getStream,
38
+ setState: (params) => withMongoError(() => setState(params)),
39
+ setImportJobState: (params) => withMongoError(() => setImportJobState(params)),
40
+ pushIds,
41
+ pushMessages,
42
+ setOperation: (params) => withMongoError(() => setOperation(params)),
43
+ setOperations: (params) => withMongoError(() => setOperations(params)),
44
+ addBlobSize: (params) => withMongoError(() => addBlobSize(params)),
45
+ setBlobSize: (params) => withMongoError(() => setBlobSize(params))
87
46
  };
88
- if (stream) {
89
- debug(`Creating streamBulk`);
90
- try {
91
- await new Promise((resolve, reject) => {
92
- const outputStream = gridFSBucket.openUploadStream(correlationId);
93
- stream.on("error", reject).on("data", (chunk) => outputStream.write(chunk)).on("end", () => outputStream.end(void 0, void 0, () => {
94
- resolve(correlationId);
95
- }));
96
- });
97
- logger.info(`New BULK queue item for ${operation} ${correlationId} has been made in ${collection}!`);
98
- debug(`New BULK queue item for ${operation} ${correlationId} has been made in ${collection}!`);
99
- return operator.insertOne(newQueueItem);
100
- } catch (error) {
101
- const errorMessage = error.payload || error.message || "";
102
- logError(error);
103
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
104
- }
105
- }
106
- logger.debug(`No stream`);
107
- debug(`Creating noStreamBulk`);
108
- if (!stream) {
109
- try {
110
- const result = await operator.insertOne(newQueueItem);
111
- if (result.acknowledged) {
112
- logger.info(`New noStream BULK queue item for ${operation} ${correlationId} has been made in ${collection}`);
113
- debug(`New noStream BULK queue item for ${operation} ${correlationId} has been made in ${collection}`);
114
- return { correlationId, queueItemState: QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS };
115
- }
116
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR);
117
- } catch (error) {
118
- const errorMessage = error.payload || error.message || "";
119
- logError(error);
120
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
121
- }
47
+ async function createPrio({ correlationId, cataloger, oCatalogerIn, operation, operationSettings }) {
48
+ debugDev('createPrio');
49
+ const time = new Date();
50
+ const newQueueItem = {
51
+ correlationId,
52
+ cataloger,
53
+ operation,
54
+ operations: [operation],
55
+ oCatalogerIn,
56
+ operationSettings: {
57
+ prio: true,
58
+ ...operationSettings
59
+ },
60
+ queueItemState: QUEUE_ITEM_STATE.VALIDATOR.PENDING_VALIDATION,
61
+ blobSize: 1,
62
+ importJobState: {
63
+ CREATE: IMPORT_JOB_STATE.EMPTY,
64
+ UPDATE: IMPORT_JOB_STATE.EMPTY,
65
+ FIX: IMPORT_JOB_STATE.EMPTY
66
+ },
67
+ records: [],
68
+ errorMessage: '',
69
+ errorStatus: '',
70
+ creationTime: time,
71
+ modificationTime: time
72
+ };
73
+ try {
74
+ debugDev(`Inserting: ${JSON.stringify(newQueueItem)} to ${collection}}`);
75
+ const result = await operator.insertOne(newQueueItem);
76
+ debugDev(JSON.stringify(result));
77
+ if (result.acknowledged) {
78
+ logger.info(`New PRIO queue item for ${operation} ${correlationId} has been made in ${collection}`);
79
+ return;
80
+ }
81
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, undefined);
82
+ }
83
+ catch (error) {
84
+ const apiError = error;
85
+ const errorMessage = apiError.payload || apiError.message || '';
86
+ logError(error);
87
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
88
+ }
122
89
  }
123
- }
124
- async function checkAndSetState({ correlationId, state, errorMessage = void 0, errorStatus = void 0 }) {
125
- const timeOut = await checkTimeOut({ correlationId });
126
- if (timeOut) {
127
- return setState({ correlationId, state, errorMessage, errorStatus });
90
+ async function createBulk({ correlationId, cataloger, oCatalogerIn, operation, contentType, recordLoadParams, stream, operationSettings }) {
91
+ const time = new Date();
92
+ const newQueueItem = {
93
+ correlationId,
94
+ cataloger,
95
+ oCatalogerIn,
96
+ operation,
97
+ operations: [operation],
98
+ operationSettings: {
99
+ prio: false,
100
+ ...operationSettings
101
+ },
102
+ contentType,
103
+ recordLoadParams,
104
+ queueItemState: stream ? QUEUE_ITEM_STATE.VALIDATOR.UPLOADING : QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS,
105
+ blobSize: 0,
106
+ importJobState: {
107
+ CREATE: IMPORT_JOB_STATE.EMPTY,
108
+ UPDATE: IMPORT_JOB_STATE.EMPTY,
109
+ FIX: IMPORT_JOB_STATE.EMPTY
110
+ },
111
+ records: [],
112
+ errorMessage: '',
113
+ errorStatus: '',
114
+ creationTime: time,
115
+ modificationTime: time
116
+ };
117
+ if (stream) {
118
+ debug(`Creating streamBulk`);
119
+ try {
120
+ await new Promise((resolve, reject) => {
121
+ const outputStream = gridFSBucket.openUploadStream(correlationId);
122
+ outputStream.on('error', (error) => {
123
+ outputStream.destroy();
124
+ reject(error);
125
+ });
126
+ stream
127
+ .on('error', (error) => {
128
+ outputStream.destroy();
129
+ reject(error);
130
+ })
131
+ .on('data', chunk => outputStream.write(chunk))
132
+ .on('end', () => outputStream.end(undefined, () => {
133
+ resolve(correlationId);
134
+ }));
135
+ });
136
+ logger.info(`New BULK queue item for ${operation} ${correlationId} has been made in ${collection}!`);
137
+ debug(`New BULK queue item for ${operation} ${correlationId} has been made in ${collection}!`);
138
+ const result = await operator.insertOne(newQueueItem);
139
+ if (!result.acknowledged) {
140
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, undefined);
141
+ }
142
+ return { correlationId, acknowledged: result.acknowledged, queueItemState: newQueueItem.queueItemState };
143
+ }
144
+ catch (error) {
145
+ const apiError = error;
146
+ const errorMessage = apiError.payload || apiError.message || '';
147
+ logError(error);
148
+ await removePartialContent(correlationId);
149
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
150
+ }
151
+ }
152
+ async function removePartialContent(correlationId) {
153
+ try {
154
+ const files = await gridFSBucket.find({ filename: correlationId }).toArray();
155
+ for (const file of files) {
156
+ await gridFSBucket.delete(file._id);
157
+ logger.debug(`mongo/createBulk: removed partial GridFS file for ${correlationId}`);
158
+ }
159
+ }
160
+ catch (cleanupError) {
161
+ const apiError = cleanupError;
162
+ logger.debug(`mongo/createBulk: partial GridFS cleanup failed for ${correlationId}: ${apiError.message}`);
163
+ }
164
+ }
165
+ logger.debug(`No stream`);
166
+ debug(`Creating noStreamBulk`);
167
+ if (!stream) {
168
+ try {
169
+ const result = await operator.insertOne(newQueueItem);
170
+ if (result.acknowledged) {
171
+ logger.info(`New noStream BULK queue item for ${operation} ${correlationId} has been made in ${collection}`);
172
+ debug(`New noStream BULK queue item for ${operation} ${correlationId} has been made in ${collection}`);
173
+ return { correlationId, acknowledged: result.acknowledged, queueItemState: QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS };
174
+ }
175
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, undefined);
176
+ }
177
+ catch (error) {
178
+ const apiError = error;
179
+ const errorMessage = apiError.payload || apiError.message || '';
180
+ logError(error);
181
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
182
+ }
183
+ }
184
+ return undefined;
128
185
  }
129
- return false;
130
- }
131
- async function checkAndSetImportJobState({ correlationId, operation, importJobState }) {
132
- logger.debug(`${correlationId}, ${importJobState}, ${operation}`);
133
- const timeOut = await checkTimeOut({ correlationId, operation, importJobState });
134
- if (timeOut) {
135
- return setImportJobState({ correlationId, operation, importJobState });
186
+ async function checkAndSetState({ correlationId, state, errorMessage = undefined, errorStatus = undefined }) {
187
+ assertString(correlationId, 'correlationId');
188
+ const timeOut = await checkTimeOut({ correlationId });
189
+ if (timeOut) {
190
+ const queueItem = await setState({ correlationId, state, errorMessage, errorStatus });
191
+ return { status: 'updated', queueItem };
192
+ }
193
+ const queueItem = await operator.findOne({ correlationId }, { projection: { _id: 0 } });
194
+ const status = queueItem && queueItem.queueItemState === QUEUE_ITEM_STATE.ABORT ? 'timeout' : 'unchanged';
195
+ return { status, queueItem };
136
196
  }
137
- return false;
138
- }
139
- async function query(params, showParams = {}) {
140
- logger.debug(`Querying: ${JSON.stringify(params)}, ${JSON.stringify(showParams)}`);
141
- debug(`Querying: ${JSON.stringify(params)}, ${JSON.stringify(showParams)}`);
142
- const { limit = 1e3, skip = 0, ...rest } = params;
143
- const result = await operator.find(rest, { projection: createProjection(showParams) }).limit(parseInt(limit, 10)).skip(parseInt(skip, 10)).toArray();
144
- logger.debug(`Query result: ${result.length > 0 ? "Found!" : "Not found!"}`);
145
- logger.silly(`${JSON.stringify(result)}`);
146
- debug(`${JSON.stringify(result)}`);
147
- return result;
148
- }
149
- function createProjection(showParams = {}) {
150
- logger.silly(`Creating projection for query: ${JSON.stringify(showParams)}`);
151
- debug(`Creating projection for query: ${JSON.stringify(showParams)}`);
152
- const { showAll = 0, showOperations = 0, showOperationSettings = 0, showRecordLoadParams = 0, showImportJobState = 0 } = showParams;
153
- const combinedShowParams = {
154
- showAll,
155
- showOperations,
156
- showOperationSettings,
157
- showRecordLoadParams,
158
- showImportJobState
159
- };
160
- logger.debug(`showAll: ${showAll}, showOperations: ${showOperations}, showOperationSettings: ${showOperationSettings}, showRecordLoadParams: ${showRecordLoadParams}, showImportJobState: ${showImportJobState}`);
161
- debug(`showAll: ${showAll}, showOperations: ${showOperations}, showOperationSettings: ${showOperationSettings}, showRecordLoadParams: ${showRecordLoadParams}, showImportJobState: ${showImportJobState}`);
162
- debug(`${JSON.stringify(combinedShowParams)}`);
163
- if (parseBoolean(showAll)) {
164
- debug(`ShowAll: ${showAll}`);
165
- return {
166
- _id: 0
167
- };
197
+ async function checkAndSetImportJobState({ correlationId, operation, importJobState }) {
198
+ assertString(correlationId, 'correlationId');
199
+ logger.debug(`${correlationId}, ${importJobState}, ${operation}`);
200
+ const timeOut = await checkTimeOut({ correlationId, operation, importJobState });
201
+ if (timeOut) {
202
+ const queueItem = await setImportJobState({ correlationId, operation, importJobState });
203
+ return { status: 'updated', queueItem };
204
+ }
205
+ const queueItem = await operator.findOne({ correlationId }, { projection: { _id: 0 } });
206
+ const status = queueItem && queueItem.queueItemState === QUEUE_ITEM_STATE.ABORT ? 'timeout' : 'unchanged';
207
+ return { status, queueItem };
168
208
  }
169
- const showParamToField = {
170
- "showOperations": "operations",
171
- "showOperationSettings": "operationSettings",
172
- "showRecordLoadParams": "recordLoadParams",
173
- "showImportJobState": "importJobState"
174
- };
175
- const result = Object.keys(combinedShowParams).filter((param) => param !== "showAll" && !parseBoolean(combinedShowParams[param])).filter((param) => showParamToField[param]).map((param) => showParamToField[param]);
176
- logger.silly(`We want to exclude from projection: ${JSON.stringify(result)}`);
177
- debug(`We want to exclude from projection: ${JSON.stringify(result)}`);
178
- const excludeObject = Object.fromEntries(result.map((param) => [param, 0]));
179
- logger.silly(`We want to exclude from projection: ${JSON.stringify(excludeObject)}`);
180
- debug(`We want to exclude from projection: ${JSON.stringify(excludeObject)}`);
181
- return {
182
- _id: 0,
183
- ...excludeObject
184
- };
185
- }
186
- async function queryById({ correlationId, checkModTime = false }) {
187
- if (checkModTime) {
188
- debug(`queryById checkModTime: ${checkModTime}`);
189
- await checkTimeOut({ correlationId });
190
- return queryById({ correlationId });
209
+ async function query(params, showParams = {}) {
210
+ logger.debug(`Querying: ${JSON.stringify(params)}, ${JSON.stringify(showParams)}`);
211
+ debug(`Querying: ${JSON.stringify(params)}, ${JSON.stringify(showParams)}`);
212
+ const { limit = 1000, skip = 0, ...rest } = params;
213
+ const result = await operator.find(rest, { projection: createProjection(showParams) })
214
+ .limit(parseInt(String(limit), 10))
215
+ .skip(parseInt(String(skip), 10))
216
+ .toArray();
217
+ logger.debug(`Query result: ${result.length > 0 ? 'Found!' : 'Not found!'}`);
218
+ logger.silly(`${JSON.stringify(result)}`);
219
+ debug(`${JSON.stringify(result)}`);
220
+ return result;
191
221
  }
192
- const result = await operator.findOne({ correlationId }, { projection: { _id: 0 } });
193
- return result;
194
- }
195
- async function checkTimeOut({ correlationId }) {
196
- const { modificationTime, queueItemState: oldState, importJobState } = await operator.findOne({ correlationId });
197
- if ([QUEUE_ITEM_STATE.ABORT, QUEUE_ITEM_STATE.ERROR, QUEUE_ITEM_STATE.DONE].includes(oldState)) {
198
- logger.silly(`${correlationId} has already state: ${oldState}`);
199
- return false;
222
+ function createProjection(showParams = {}) {
223
+ logger.silly(`Creating projection for query: ${JSON.stringify(showParams)}`);
224
+ debug(`Creating projection for query: ${JSON.stringify(showParams)}`);
225
+ const { showAll = 0, showOperations = 0, showOperationSettings = 0, showRecordLoadParams = 0, showImportJobState = 0 } = showParams;
226
+ const combinedShowParams = {
227
+ showAll,
228
+ showOperations,
229
+ showOperationSettings,
230
+ showRecordLoadParams,
231
+ showImportJobState
232
+ };
233
+ logger.debug(`showAll: ${showAll}, showOperations: ${showOperations}, showOperationSettings: ${showOperationSettings}, showRecordLoadParams: ${showRecordLoadParams}, showImportJobState: ${showImportJobState}`);
234
+ debug(`showAll: ${showAll}, showOperations: ${showOperations}, showOperationSettings: ${showOperationSettings}, showRecordLoadParams: ${showRecordLoadParams}, showImportJobState: ${showImportJobState}`);
235
+ debug(`${JSON.stringify(combinedShowParams)}`);
236
+ if (parseBoolean(showAll)) {
237
+ debug(`ShowAll: ${showAll}`);
238
+ return {
239
+ _id: 0
240
+ };
241
+ }
242
+ const showParamToField = {
243
+ 'showOperations': 'operations',
244
+ 'showOperationSettings': 'operationSettings',
245
+ 'showRecordLoadParams': 'recordLoadParams',
246
+ 'showImportJobState': 'importJobState'
247
+ };
248
+ const result = Object.keys(combinedShowParams)
249
+ .filter(param => param !== 'showAll' && !parseBoolean(combinedShowParams[param]))
250
+ .filter((param) => param in showParamToField)
251
+ .map(param => showParamToField[param]);
252
+ logger.silly(`We want to exclude from projection: ${JSON.stringify(result)}`);
253
+ debug(`We want to exclude from projection: ${JSON.stringify(result)}`);
254
+ const excludeObject = Object.fromEntries(result.map(param => [param, 0]));
255
+ logger.silly(`We want to exclude from projection: ${JSON.stringify(excludeObject)}`);
256
+ debug(`We want to exclude from projection: ${JSON.stringify(excludeObject)}`);
257
+ return {
258
+ _id: 0,
259
+ ...excludeObject
260
+ };
200
261
  }
201
- const timeoutTime = moment(modificationTime).add(1, "m");
202
- logger.silly(`${correlationId} timeOut @ ${timeoutTime}`);
203
- if (timeoutTime.isBefore()) {
204
- const finalImportJobStates = [IMPORT_JOB_STATE.ABORT, IMPORT_JOB_STATE.DONE, IMPORT_JOB_STATE.EMPTY, IMPORT_JOB_STATE.ERROR];
205
- if (!finalImportJobStates.includes(importJobState.CREATE)) {
206
- await setImportJobState({ correlationId, operation: "CREATE", importJobState: IMPORT_JOB_STATE.ABORT });
207
- }
208
- if (!finalImportJobStates.includes(importJobState.UPDATE)) {
209
- await setImportJobState({ correlationId, operation: "UPDATE", importJobState: IMPORT_JOB_STATE.ABORT });
210
- }
211
- if (!finalImportJobStates.includes(importJobState.FIX)) {
212
- await setImportJobState({ correlationId, operation: "FIX", importJobState: IMPORT_JOB_STATE.ABORT });
213
- }
214
- await setState({ correlationId, state: QUEUE_ITEM_STATE.ABORT, errorStatus: httpStatus.REQUEST_TIMEOUT, errorMessage: `Timeout in ${oldState}` });
215
- return false;
262
+ async function queryById({ correlationId, checkModTime = false }) {
263
+ assertString(correlationId, 'correlationId');
264
+ if (checkModTime) {
265
+ debug(`queryById checkModTime: ${checkModTime}`);
266
+ await checkTimeOut({ correlationId });
267
+ return queryById({ correlationId });
268
+ }
269
+ const result = await operator.findOne({ correlationId }, { projection: { _id: 0 } });
270
+ return result;
216
271
  }
217
- return true;
218
- }
219
- async function remove(params) {
220
- logger.silly(`${JSON.stringify(params)}`);
221
- logger.info(`Removing from Mongo (${collection}) id: ${params.correlationId}`);
222
- debug(`${JSON.stringify(params)}`);
223
- debug(`Removing from Mongo (${collection}) id: ${params.correlationId}`);
224
- const clean = sanitize(params.correlationId);
225
- logger.silly(`mongo/remove: clean: ${JSON.stringify(clean)}`);
226
- debug(`mongo/remove: clean: ${JSON.stringify(clean)}`);
227
- try {
228
- const noContent = await removeContent({ correlationId: params.correlationId });
229
- debug(`noContent (removeContent result): ${JSON.stringify(noContent)}`);
230
- if (noContent) {
231
- const deleteResult = await operator.deleteOne({ correlationId: clean });
232
- debug(`deleteResult: ${JSON.stringify(deleteResult)}`);
272
+ async function checkTimeOut({ correlationId }) {
273
+ assertString(correlationId, 'correlationId');
274
+ const queueItem = await operator.findOne({ correlationId });
275
+ if (!queueItem) {
276
+ logger.silly(`${correlationId} not found in ${collection} - not a timeout`);
277
+ return false;
278
+ }
279
+ const { modificationTime, queueItemState: oldState, importJobState = { CREATE: IMPORT_JOB_STATE.EMPTY, UPDATE: IMPORT_JOB_STATE.EMPTY, FIX: IMPORT_JOB_STATE.EMPTY } } = queueItem;
280
+ if (oldState === QUEUE_ITEM_STATE.ABORT || oldState === QUEUE_ITEM_STATE.ERROR || oldState === QUEUE_ITEM_STATE.DONE) {
281
+ logger.silly(`${correlationId} has already state: ${oldState}`);
282
+ return false;
283
+ }
284
+ const timeoutTime = new Date(new Date(modificationTime).getTime() + timeoutMinutes * 60000);
285
+ logger.silly(`${correlationId} timeOut @ ${timeoutTime}`);
286
+ if (timeoutTime < new Date()) {
287
+ const finalImportJobStates = [IMPORT_JOB_STATE.ABORT, IMPORT_JOB_STATE.DONE, IMPORT_JOB_STATE.EMPTY, IMPORT_JOB_STATE.ERROR];
288
+ if (!finalImportJobStates.includes(importJobState.CREATE)) {
289
+ await setImportJobState({ correlationId, operation: 'CREATE', importJobState: IMPORT_JOB_STATE.ABORT });
290
+ }
291
+ if (!finalImportJobStates.includes(importJobState.UPDATE)) {
292
+ await setImportJobState({ correlationId, operation: 'UPDATE', importJobState: IMPORT_JOB_STATE.ABORT });
293
+ }
294
+ if (!finalImportJobStates.includes(importJobState.FIX)) {
295
+ await setImportJobState({ correlationId, operation: 'FIX', importJobState: IMPORT_JOB_STATE.ABORT });
296
+ }
297
+ await setState({ correlationId, state: QUEUE_ITEM_STATE.ABORT, errorStatus: httpStatus.REQUEST_TIMEOUT, errorMessage: `Timeout in ${oldState}` });
298
+ return false;
299
+ }
233
300
  return true;
234
- }
235
- } catch (err) {
236
- if (err instanceof MongoDriverError) {
237
- debug(err);
238
- if (err.message.indexOf("File not found for id") !== -1) {
239
- logger.silly(`mongo/remove: File not found, removing queueItem ${JSON.stringify(clean)} from ${collection}`);
240
- debug(`mongo/remove: File not found, removing queueItem ${JSON.stringify(clean)} from ${collection}`);
241
- await operator.deleteOne({ correlationId: clean });
242
- return true;
243
- }
244
- logger.error(err.message);
245
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, err.message);
246
- }
247
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, err.message);
248
301
  }
249
- }
250
- async function readContent(correlationId) {
251
- logger.info(`Reading content from mongo for id: ${correlationId} in ${collection}`);
252
- debug(`Reading content from mongo for id: ${correlationId} in ${collection}`);
253
- const clean = sanitize(correlationId);
254
- debug(clean);
255
- const result = await operator.findOne({ correlationId: clean });
256
- debug(result);
257
- if (result) {
258
- const { operationSettings } = result;
259
- if (operationSettings.prio === false && operationSettings.noStream === false) {
260
- return {
261
- contentType: result.contentType,
262
- readStream: gridFSBucket.openDownloadStreamByName(clean)
302
+ async function remove(params) {
303
+ logger.silly(`${JSON.stringify(params)}`);
304
+ logger.info(`Removing from Mongo (${collection}) id: ${params.correlationId}`);
305
+ debug(`${JSON.stringify(params)}`);
306
+ debug(`Removing from Mongo (${collection}) id: ${params.correlationId}`);
307
+ assertString(params.correlationId, 'correlationId');
308
+ try {
309
+ const removedContent = await removeContent({ correlationId: params.correlationId });
310
+ debug(`removedContent (removeContent result): ${JSON.stringify(removedContent)}`);
311
+ const deleteResult = await operator.deleteOne({ correlationId: params.correlationId });
312
+ debug(`deleteResult: ${JSON.stringify(deleteResult)}`);
313
+ return { removed: deleteResult.deletedCount > 0, deletedContent: removedContent };
314
+ }
315
+ catch (err) {
316
+ if (err instanceof MongoDriverError) {
317
+ debug(err);
318
+ if (err.message.indexOf('File not found for id') !== -1) {
319
+ logger.silly(`mongo/remove: File not found, removing queueItem ${JSON.stringify(params.correlationId)} from ${collection}`);
320
+ debug(`mongo/remove: File not found, removing queueItem ${JSON.stringify(params.correlationId)} from ${collection}`);
321
+ const deleteResult = await operator.deleteOne({ correlationId: params.correlationId });
322
+ return { removed: deleteResult.deletedCount > 0, deletedContent: false };
323
+ }
324
+ logger.error(err.message);
325
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, err.message);
326
+ }
327
+ const driverError = err;
328
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, driverError.message);
329
+ }
330
+ }
331
+ async function readContent(correlationId) {
332
+ logger.info(`Reading content from mongo for id: ${correlationId} in ${collection}`);
333
+ debug(`Reading content from mongo for id: ${correlationId} in ${collection}`);
334
+ assertString(correlationId, 'correlationId');
335
+ const result = await operator.findOne({ correlationId });
336
+ debug(result);
337
+ if (result) {
338
+ const { operationSettings } = result;
339
+ let files = [];
340
+ if (operationSettings.prio === false) {
341
+ files = await gridFSBucket.find({ filename: correlationId }).toArray();
342
+ }
343
+ const hasContent = files.length > 0;
344
+ if (hasContent) {
345
+ return {
346
+ contentType: result.contentType,
347
+ readStream: gridFSBucket.openDownloadStreamByName(correlationId)
348
+ };
349
+ }
350
+ logger.debug(`OperationSettings for ${correlationId}: ${JSON.stringify(operationSettings)}`);
351
+ debug(`OperationSettings for ${correlationId}: ${JSON.stringify(operationSettings)}`);
352
+ debug(`Content is only available for streamBulk jobs. ${correlationId} is not a streamBulk job.`);
353
+ throw new ApiError(httpStatus.BAD_REQUEST, { message: `Content is only available for streamBulk jobs. ${correlationId} is not a streamBulk job.` });
354
+ }
355
+ throw new ApiError(httpStatus.NOT_FOUND, { message: `Job ${correlationId} not found.` });
356
+ }
357
+ async function removeContent(params) {
358
+ logger.info(`Removing content from mongo for id: ${params.correlationId} in ${collection}`);
359
+ debug(`Removing content from mongo for id: ${params.correlationId} in ${collection}`);
360
+ assertString(params.correlationId, 'correlationId');
361
+ const result = await gridFSBucket.find({ filename: params.correlationId }).toArray();
362
+ debug(`mongo/removeContent: find bucket result ${JSON.stringify(result)}`);
363
+ if (result.length > 0) {
364
+ const [firstBucketResult] = result;
365
+ const fileId = firstBucketResult._id;
366
+ await gridFSBucket.delete(fileId);
367
+ return true;
368
+ }
369
+ logger.debug(`No files for ${params.correlationId} to delete`);
370
+ return false;
371
+ }
372
+ async function getOne({ queueItemState, importJobState = undefined }) {
373
+ logger.silly(`queueItemState: ${queueItemState}, importJobState: ${JSON.stringify(importJobState)}`);
374
+ if (queueItemState) {
375
+ assertString(queueItemState, 'queueItemState');
376
+ }
377
+ if (importJobState) {
378
+ assertImportJobStateFilter(importJobState, 'importJobState');
379
+ }
380
+ const cleanQueueItemState = queueItemState ? { queueItemState } : undefined;
381
+ const options = { projection: { _id: 0 } };
382
+ try {
383
+ if (queueItemState && importJobState === undefined) {
384
+ logger.silly(`Checking DB ${collection} for just ${JSON.stringify(queueItemState)}`);
385
+ return operator.findOne({ queueItemState }, options);
386
+ }
387
+ if (importJobState && queueItemState === undefined) {
388
+ logger.silly(`Checking DB ${collection} for ${JSON.stringify(importJobState)}`);
389
+ return operator.findOne({ ...importJobState }, options);
390
+ }
391
+ if (importJobState && queueItemState) {
392
+ logger.silly(`Checking DB ${collection} for ${queueItemState} and ${JSON.stringify(importJobState)}`);
393
+ return operator.findOne({ ...cleanQueueItemState, ...importJobState }, options);
394
+ }
395
+ logger.debug(`getOne not working!`);
396
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Missing parameters, cannot get queueItem');
397
+ }
398
+ catch (error) {
399
+ const apiError = error;
400
+ const errorMessage = apiError.payload || apiError.message || '';
401
+ logError(error);
402
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
403
+ }
404
+ }
405
+ async function getStream(correlationId) {
406
+ logger.info(`Forming stream from mongo for ${correlationId} in ${collection}`);
407
+ assertString(correlationId, 'correlationId');
408
+ try {
409
+ const files = await gridFSBucket.find({ filename: correlationId }).toArray();
410
+ if (files.length === 0) {
411
+ logger.info(`getStream: file ${correlationId} not found in ${collection}`);
412
+ throw new ApiError(httpStatus.NOT_FOUND, { message: `File ${correlationId} not found in ${collection}` });
413
+ }
414
+ return gridFSBucket.openDownloadStreamByName(correlationId);
415
+ }
416
+ catch (error) {
417
+ if (error instanceof ApiError) {
418
+ throw error;
419
+ }
420
+ const apiError = error;
421
+ const errorMessage = apiError.payload || apiError.message || '';
422
+ logError(error);
423
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
424
+ }
425
+ }
426
+ async function pushIds({ correlationId, handledIds, rejectedIds }) {
427
+ if (!Array.isArray(handledIds) || !Array.isArray(rejectedIds)) {
428
+ throw new ApiError(httpStatus.BAD_REQUEST, 'pushIds: handledIds and rejectedIds must be arrays');
429
+ }
430
+ assertString(correlationId, 'correlationId');
431
+ logger.verbose(`Push ids (${handledIds.length}) and rejectedIds (${rejectedIds.length}) ${correlationId} to ${collection}`);
432
+ logger.debug(`ids (${handledIds.length}): ${JSON.stringify(handledIds)}, rejectedIds ${rejectedIds.length}: ${JSON.stringify(rejectedIds)}`);
433
+ try {
434
+ await operator.updateOne({
435
+ correlationId
436
+ }, {
437
+ $set: {
438
+ modificationTime: new Date()
439
+ },
440
+ $push: {
441
+ handledIds: { $each: handledIds },
442
+ rejectedIds: { $each: rejectedIds }
443
+ }
444
+ });
445
+ }
446
+ catch (error) {
447
+ const apiError = error;
448
+ const errorMessage = apiError.payload || apiError.message || '';
449
+ logError(error);
450
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
451
+ }
452
+ }
453
+ async function pushMessages({ correlationId, messages, messageField = 'messages' }) {
454
+ if (!Array.isArray(messages)) {
455
+ throw new ApiError(httpStatus.BAD_REQUEST, 'pushMessages: messages must be an array');
456
+ }
457
+ assertString(correlationId, 'correlationId');
458
+ assertString(messageField, 'messageField');
459
+ logger.verbose(`Push messages (${messages.length}) to ${messageField} ${correlationId} to ${collection}`);
460
+ logger.silly(`Messages (${messages.length}): ${JSON.stringify(messages)}`);
461
+ try {
462
+ await operator.updateOne({
463
+ correlationId
464
+ }, {
465
+ $set: {
466
+ modificationTime: new Date()
467
+ },
468
+ $push: {
469
+ [messageField]: { $each: messages }
470
+ }
471
+ });
472
+ }
473
+ catch (error) {
474
+ const apiError = error;
475
+ const errorMessage = apiError.payload || apiError.message || '';
476
+ logError(error);
477
+ throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
478
+ }
479
+ }
480
+ function setState({ correlationId, state, errorMessage = undefined, errorStatus = undefined }) {
481
+ assertString(correlationId, 'correlationId');
482
+ const errorString = errorMessage || errorStatus ? `, Error message: '${JSON.stringify(errorMessage) || ''}', Error status: '${errorStatus || ''}'` : '';
483
+ logger.info(`Setting queue-item state ${state} for ${correlationId}${errorString} to ${collection}`);
484
+ const allQueueItemStates = [
485
+ QUEUE_ITEM_STATE.DONE,
486
+ QUEUE_ITEM_STATE.ERROR,
487
+ QUEUE_ITEM_STATE.ABORT,
488
+ ...Object.values(QUEUE_ITEM_STATE.VALIDATOR),
489
+ ...Object.values(QUEUE_ITEM_STATE.IMPORTER)
490
+ ];
491
+ if (!allQueueItemStates.includes(state)) {
492
+ debug(`Trying to set invalid state ${state}`);
493
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Trying to set invalid state');
494
+ }
495
+ const updateValues = {
496
+ queueItemState: state,
497
+ modificationTime: new Date(),
498
+ errorMessage,
499
+ errorStatus
263
500
  };
264
- }
265
- logger.debug(`OperationSettings for ${correlationId}: ${JSON.stringify(operationSettings)}`);
266
- debug(`OperationSettings for ${correlationId}: ${JSON.stringify(operationSettings)}`);
267
- debug(`Content is only available for streamBulk jobs. ${correlationId} is not a streamBulk job.`);
268
- throw new ApiError(httpStatus.BAD_REQUEST, { message: `Content is only available for streamBulk jobs. ${correlationId} is not a streamBulk job.` });
501
+ Object.keys(updateValues).forEach(key => updateValues[key] === undefined && delete updateValues[key]);
502
+ return operator
503
+ .findOneAndUpdate({ correlationId }, { $set: updateValues }, { projection: { _id: 0 }, returnDocument: 'after' });
269
504
  }
270
- throw new ApiError(httpStatus.NOT_FOUND, { message: `Job ${correlationId} not found.` });
271
- }
272
- async function removeContent(params) {
273
- logger.info(`Removing content from mongo for id: ${params.correlationId} in ${collection}`);
274
- debug(`Removing content from mongo for id: ${params.correlationId} in ${collection}`);
275
- const clean = sanitize(params.correlationId);
276
- const result = await gridFSBucket.find({ filename: clean }).toArray();
277
- debug(`mongo/removeContent: find bucket result ${JSON.stringify(result)}`);
278
- if (result.length > 0) {
279
- const [firstBucketResult] = result;
280
- const fileId = firstBucketResult._id;
281
- await gridFSBucket.delete(fileId);
282
- return true;
505
+ function setImportJobState({ correlationId, operation, importJobState }) {
506
+ assertString(correlationId, 'correlationId');
507
+ logger.info(`Setting queue-item importJobState: {${operation}: ${importJobState}} for ${correlationId}`);
508
+ debug(`Setting queue-item importJobState: {${operation}: ${importJobState}} for ${correlationId}`);
509
+ if (!(operation in OPERATIONS)) {
510
+ debug(`Error: ${operation} is not in ${JSON.stringify(OPERATIONS)}`);
511
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid operation for import job state');
512
+ }
513
+ if (!(importJobState in IMPORT_JOB_STATE)) {
514
+ debug(`Error: ${importJobState} is not in ${JSON.stringify(IMPORT_JOB_STATE)}`);
515
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid import job state');
516
+ }
517
+ const newJobState = getNewJobState(operation, importJobState);
518
+ return operator.findOneAndUpdate({
519
+ correlationId
520
+ }, {
521
+ $set: {
522
+ ...newJobState,
523
+ modificationTime: new Date()
524
+ }
525
+ }, { projection: { _id: 0 }, returnDocument: 'after' });
283
526
  }
284
- logger.debug(`No files for ${params.correlationId} to delete`);
285
- return true;
286
- }
287
- function getOne({ queueItemState, importJobState = void 0 }) {
288
- logger.silly(`queueItemState: ${queueItemState}, importJobState: ${JSON.stringify(importJobState)}`);
289
- const cleanQueueItemState = queueItemState ? { queueItemState: sanitize(queueItemState) } : void 0;
290
- const options = { projection: { _id: 0 } };
291
- try {
292
- if (queueItemState && importJobState === void 0) {
293
- logger.silly(`Checking DB ${collection} for just ${JSON.stringify(cleanQueueItemState.queueItemState)}`);
294
- return operator.findOne({ ...cleanQueueItemState }, options);
295
- }
296
- if (importJobState && queueItemState === void 0) {
297
- logger.silly(`Checking DB ${collection} for ${JSON.stringify(importJobState)}`);
298
- return operator.findOne({ ...importJobState }, options);
299
- }
300
- if (importJobState && queueItemState) {
301
- logger.silly(`Checking DB ${collection} for ${queueItemState} and ${JSON.stringify(importJobState)}`);
302
- return operator.findOne({ ...cleanQueueItemState, ...importJobState }, options);
303
- }
304
- logger.debug(`getOne not working!`);
305
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, "Missing parameters, cannot get queueItem");
306
- } catch (error) {
307
- const errorMessage = error.payload || error.message || "";
308
- logError(error);
309
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
527
+ function getNewJobState(operation, importJobState) {
528
+ if (operation === OPERATIONS.CREATE) {
529
+ return { 'importJobState.CREATE': importJobState };
530
+ }
531
+ if (operation === OPERATIONS.UPDATE) {
532
+ return { 'importJobState.UPDATE': importJobState };
533
+ }
534
+ if (operation === OPERATIONS.FIX) {
535
+ return { 'importJobState.FIX': importJobState };
536
+ }
537
+ throw new ApiError(httpStatus.BAD_REQUEST, 'Invalid operation for import job state');
310
538
  }
311
- }
312
- function getStream(correlationId) {
313
- logger.info(`Forming stream from mongo for ${correlationId} in ${collection}`);
314
- const clean = sanitize(correlationId);
315
- try {
316
- return gridFSBucket.openDownloadStreamByName(clean);
317
- } catch (error) {
318
- const errorMessage = error.payload || error.message || "";
319
- logError(error);
320
- throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Mongo errored: ${errorMessage}`);
539
+ function unwrapAndUpdateResult(result, correlationId) {
540
+ if (result === null || result === undefined) {
541
+ logger.info(`Queue item ${correlationId} not found in ${collection}`);
542
+ throw new ApiError(httpStatus.NOT_FOUND, { message: `Job ${correlationId} not found.` });
543
+ }
544
+ return result;
321
545
  }
322
- }
323
- async function pushIds({ correlationId, handledIds, rejectedIds }) {
324
- logger.verbose(`Push ids (${handledIds.length}) and rejectedIds (${rejectedIds.length}) ${correlationId} to ${collection}`);
325
- logger.debug(`ids (${handledIds.length}): ${JSON.stringify(handledIds)}, rejectedIds ${rejectedIds.length}: ${JSON.stringify(rejectedIds)}`);
326
- const clean = sanitize(correlationId);
327
- await operator.updateOne({
328
- correlationId: clean
329
- }, {
330
- $set: {
331
- modificationTime: moment().toDate()
332
- },
333
- $push: {
334
- handledIds: { $each: handledIds },
335
- rejectedIds: { $each: rejectedIds }
336
- }
337
- });
338
- }
339
- async function pushMessages({ correlationId, messages, messageField = "messages" }) {
340
- logger.verbose(`Push messages (${messages.length}) to ${messageField} ${correlationId} to ${collection}`);
341
- logger.silly(`Messages (${messages.length}): ${JSON.stringify(messages)}}`);
342
- const clean = sanitize(correlationId);
343
- const cleanMessageField = sanitize(messageField);
344
- debug(`${messageField} -> ${cleanMessageField}`);
345
- await operator.updateOne({
346
- correlationId: clean
347
- }, {
348
- $set: {
349
- modificationTime: moment().toDate()
350
- },
351
- $push: {
352
- [cleanMessageField]: { $each: messages }
353
- }
354
- });
355
- }
356
- function setState({ correlationId, state, errorMessage = void 0, errorStatus = void 0 }) {
357
- const errorString = errorMessage || errorStatus ? `, Error message: '${JSON.stringify(errorMessage) || ""}', Error status: '${errorStatus || ""}'` : "";
358
- logger.info(`Setting queue-item state ${state} for ${correlationId}${errorString} to ${collection}`);
359
- const stateInQueueItemStates = Object.values(QUEUE_ITEM_STATE).indexOf(state) > -1;
360
- const stateInQueueItemStatesValidator = Object.values(QUEUE_ITEM_STATE.VALIDATOR).indexOf(state) > -1;
361
- const stateInQueueItemStatesImporter = Object.values(QUEUE_ITEM_STATE.IMPORTER).indexOf(state) > -1;
362
- if (!stateInQueueItemStates && !stateInQueueItemStatesValidator && !stateInQueueItemStatesImporter) {
363
- debug(`Trying to set invalid state ${state}`);
364
- throw new ApiError(httpStatus.BAD_REQUEST, "Trying to set invalid state");
546
+ async function setOperation({ correlationId, operation }) {
547
+ assertString(correlationId, 'correlationId');
548
+ const newOperation = operation;
549
+ const existing = await operator.findOne({ correlationId });
550
+ if (!existing) {
551
+ logger.info(`Setting queue-item operation: queue item ${correlationId} not found in ${collection}`);
552
+ throw new ApiError(httpStatus.NOT_FOUND, { message: `Job ${correlationId} not found.` });
553
+ }
554
+ const { operation: oldOperation } = existing;
555
+ logger.info(`Setting queue-item operation from ${oldOperation} to ${newOperation} for ${correlationId} to ${collection}`);
556
+ const result = await operator.findOneAndUpdate({
557
+ correlationId
558
+ }, {
559
+ $set: {
560
+ operation: newOperation,
561
+ modificationTime: new Date()
562
+ }
563
+ }, { projection: { _id: 0 }, returnDocument: 'after' });
564
+ debugDev(`${JSON.stringify(result)}`);
565
+ return unwrapAndUpdateResult(result, correlationId);
365
566
  }
366
- const clean = sanitize(correlationId);
367
- const updateValues = {
368
- queueItemState: state,
369
- modificationTime: moment().toDate(),
370
- errorMessage,
371
- errorStatus
372
- };
373
- Object.keys(updateValues).forEach((key) => updateValues[key] === void 0 && delete updateValues[key]);
374
- return operator.findOneAndUpdate(
375
- { correlationId: clean },
376
- { $set: updateValues },
377
- { projection: { _id: 0 }, returnDocument: "after" }
378
- );
379
- }
380
- function setImportJobState({ correlationId, operation, importJobState }) {
381
- logger.info(`Setting queue-item importJobState: {${operation}: ${importJobState}} for ${correlationId}`);
382
- debug(`Setting queue-item importJobState: {${operation}: ${importJobState}} for ${correlationId}`);
383
- const cleanCorrelationId = sanitize(correlationId);
384
- const cleanImportJobState = sanitize(importJobState);
385
- if (!(operation in OPERATIONS)) {
386
- debug(`Error: ${operation} is not in ${JSON.stringify(OPERATIONS)}`);
387
- throw new ApiError(httpStatus.BAD_REQUEST, "Invalid operation for import job state");
567
+ async function setOperations({ correlationId, addOperation, removeOperation = undefined }) {
568
+ assertString(correlationId, 'correlationId');
569
+ const queueItem = await operator.findOne({ correlationId });
570
+ logger.silly(`We found a queueItem: ${JSON.stringify(queueItem)}`);
571
+ if (!queueItem) {
572
+ logger.info(`Setting queue-item operations: queue item ${correlationId} not found in ${collection}`);
573
+ throw new ApiError(httpStatus.NOT_FOUND, { message: `Job ${correlationId} not found.` });
574
+ }
575
+ const oldOperations = queueItem.operations;
576
+ logger.info(`Setting queue-item operations from ${JSON.stringify(oldOperations)} by adding ${addOperation} and removing ${removeOperation} for ${correlationId} to ${collection}`);
577
+ const operationsAfterRemove = oldOperations.filter(operation => operation !== removeOperation);
578
+ const operationsAfterRemoveAndAdd = operationsAfterRemove.includes(addOperation) ? operationsAfterRemove : [...operationsAfterRemove, addOperation];
579
+ logger.silly(`operationsAfterRemove: ${operationsAfterRemove}`);
580
+ logger.silly(`operationAfterRemoveAndAdd: ${operationsAfterRemoveAndAdd}`);
581
+ const result = await operator.findOneAndUpdate({
582
+ correlationId
583
+ }, {
584
+ $set: {
585
+ operations: operationsAfterRemoveAndAdd,
586
+ modificationTime: new Date()
587
+ }
588
+ }, { projection: { _id: 0 }, returnDocument: 'after' });
589
+ debugDev(`${JSON.stringify(result)}`);
590
+ return unwrapAndUpdateResult(result, correlationId);
388
591
  }
389
- if (!(cleanImportJobState in IMPORT_JOB_STATE)) {
390
- debug(`Error: ${cleanImportJobState} is not in ${JSON.stringify(IMPORT_JOB_STATE)}`);
391
- throw new ApiError(httpStatus.BAD_REQUEST, "Invalid import job state");
592
+ async function addBlobSize({ correlationId }) {
593
+ assertString(correlationId, 'correlationId');
594
+ const result = await operator.findOneAndUpdate({
595
+ correlationId,
596
+ queueItemState: QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS
597
+ }, {
598
+ $inc: {
599
+ blobSize: 1
600
+ },
601
+ $set: {
602
+ modificationTime: new Date()
603
+ }
604
+ }, { projection: { _id: 0 }, returnDocument: 'after' });
605
+ logger.verbose(`AddBlobSizeResult in mongo: ${JSON.stringify(result)}`);
606
+ debugDev(`${JSON.stringify(result)}`);
607
+ return result;
392
608
  }
393
- const newJobState = getNewJobState(operation, cleanImportJobState);
394
- function getNewJobState(operation2, cleanImportJobState2) {
395
- if (operation2 === OPERATIONS.CREATE) {
396
- return { "importJobState.CREATE": cleanImportJobState2 };
397
- }
398
- if (operation2 === OPERATIONS.UPDATE) {
399
- return { "importJobState.UPDATE": cleanImportJobState2 };
400
- }
401
- if (operation2 === OPERATIONS.FIX) {
402
- return { "importJobState.FIX": cleanImportJobState2 };
403
- }
404
- throw new ApiError("400", "Invalid operation for import job state");
609
+ async function setBlobSize({ correlationId, blobSize }) {
610
+ assertString(correlationId, 'correlationId');
611
+ assertNumber(blobSize, 'blobSize');
612
+ const result = await operator.findOneAndUpdate({
613
+ correlationId
614
+ }, {
615
+ $set: {
616
+ blobSize,
617
+ modificationTime: new Date()
618
+ }
619
+ }, { projection: { _id: 0 }, returnDocument: 'after' });
620
+ logger.verbose(`SetBlobSizeResult in mongo: ${JSON.stringify(result)}`);
621
+ debugDev(`${JSON.stringify(result)}`);
622
+ return result;
405
623
  }
406
- return operator.findOneAndUpdate({
407
- correlationId: cleanCorrelationId
408
- }, {
409
- $set: {
410
- ...newJobState,
411
- modificationTime: moment().toDate()
412
- }
413
- }, { projection: { _id: 0 }, returnDocument: "after" });
414
- }
415
- async function setOperation({ correlationId, operation }) {
416
- const newOperation = operation;
417
- const { operation: oldOperation } = await operator.findOne({ correlationId });
418
- logger.info(`Setting queue-item operation from ${oldOperation} to ${newOperation} for ${correlationId} to ${collection}`);
419
- const cleanCorrelationId = sanitize(correlationId);
420
- const cleanNewOperation = sanitize(newOperation);
421
- const result = await operator.findOneAndUpdate({
422
- correlationId: cleanCorrelationId
423
- }, {
424
- $set: {
425
- operation: cleanNewOperation,
426
- modificationTime: moment().toDate()
427
- }
428
- }, { projection: { _id: 0 }, returnDocument: "after" });
429
- debugDev(`${JSON.stringify(result)}`);
430
- return result.ok || result;
431
- }
432
- async function setOperations({ correlationId, addOperation, removeOperation = void 0 }) {
433
- const cleanCorrelationId = sanitize(correlationId);
434
- const queueItem = await operator.findOne({ correlationId: cleanCorrelationId });
435
- logger.silly(`We found a queueItem: ${JSON.stringify(queueItem)}`);
436
- const oldOperations = queueItem.operations;
437
- logger.info(`Setting queue-item operations from ${JSON.stringify(oldOperations)} by adding ${addOperation} and removing ${removeOperation} for ${correlationId} to ${collection}`);
438
- const operationsAfterRemove = oldOperations.filter((operation) => operation !== removeOperation);
439
- const operationsAfterRemoveAndAdd = operationsAfterRemove.includes(addOperation) ? operationsAfterRemove : [...operationsAfterRemove, addOperation];
440
- logger.silly(`operationsAfterRemove: ${operationsAfterRemove}`);
441
- logger.silly(`operationAfterRemoveAndAdd: ${operationsAfterRemoveAndAdd}`);
442
- const result = await operator.findOneAndUpdate({
443
- correlationId: cleanCorrelationId
444
- }, {
445
- $set: {
446
- operations: operationsAfterRemoveAndAdd,
447
- modificationTime: moment().toDate()
448
- }
449
- }, { projection: { _id: 0 }, returnDocument: "after" });
450
- debugDev(`${JSON.stringify(result)}`);
451
- return result.ok || result;
452
- }
453
- async function addBlobSize({ correlationId }) {
454
- const cleanCorrelationId = sanitize(correlationId);
455
- const result = await operator.findOneAndUpdate({
456
- correlationId: cleanCorrelationId,
457
- queueItemState: QUEUE_ITEM_STATE.VALIDATOR.WAITING_FOR_RECORDS
458
- }, {
459
- $inc: {
460
- blobSize: 1
461
- },
462
- $set: {
463
- modificationTime: moment().toDate()
464
- }
465
- }, { projection: { _id: 0 }, returnDocument: "after" });
466
- logger.verbose(`AddBlobSizeResult in mongo: ${JSON.stringify(result)}`);
467
- debugDev(`${JSON.stringify(result)}`);
468
- return result;
469
- }
470
- async function setBlobSize({ correlationId, blobSize }) {
471
- const cleanCorrelationId = sanitize(correlationId);
472
- const cleanBlobSize = sanitize(blobSize);
473
- const result = await operator.findOneAndUpdate({
474
- correlationId: cleanCorrelationId
475
- }, {
476
- $set: {
477
- blobSize: cleanBlobSize,
478
- modificationTime: moment().toDate()
479
- }
480
- }, { projection: { _id: 0 }, returnDocument: "after" });
481
- logger.verbose(`SetBlobSizeResult in mongo: ${JSON.stringify(result)}`);
482
- debugDev(`${JSON.stringify(result)}`);
483
- return result;
484
- }
485
624
  }
486
- //# sourceMappingURL=mongo.js.map
625
+ //# sourceMappingURL=mongo.js.map