@natlibfi/melinda-rest-api-commons 2.3.2-alpha.1 → 2.3.2-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/amqp.health.spec.js +69 -0
- package/dist/amqp.health.spec.js.map +1 -0
- package/dist/amqp.js +135 -92
- package/dist/amqp.js.map +1 -1
- package/dist/amqp.tests.spec.js +338 -0
- package/dist/amqp.tests.spec.js.map +1 -0
- package/dist/constants.js +5 -3
- package/dist/constants.js.map +1 -1
- package/dist/helpers/conversion.js +5 -25
- package/dist/helpers/conversion.js.map +1 -1
- package/dist/helpers/conversion.spec.js +1 -7
- package/dist/helpers/conversion.spec.js.map +1 -1
- package/dist/helpers/fix-constants.js +98 -0
- package/dist/helpers/fix-constants.js.map +1 -0
- package/dist/helpers/fix-handle-tempurns.js +153 -0
- package/dist/helpers/fix-handle-tempurns.js.map +1 -0
- package/dist/helpers/fix-strip-f884s.js +87 -0
- package/dist/helpers/fix-strip-f884s.js.map +1 -0
- package/dist/helpers/fix-utils.js +82 -0
- package/dist/helpers/fix-utils.js.map +1 -0
- package/dist/helpers/fix.js +178 -0
- package/dist/helpers/fix.js.map +1 -0
- package/dist/helpers/fix.spec.js +46 -0
- package/dist/helpers/fix.spec.js.map +1 -0
- package/dist/helpers/validation.js +0 -6
- package/dist/helpers/validation.js.map +1 -1
- package/dist/helpers/validation.spec.js +1 -7
- package/dist/helpers/validation.spec.js.map +1 -1
- package/dist/index.js +12 -18
- package/dist/index.js.map +1 -1
- package/dist/mongo.js +50 -89
- package/dist/mongo.js.map +1 -1
- package/dist/mongo.spec.js.draft +54 -0
- package/dist/mongoLog.js +16 -35
- package/dist/mongoLog.js.map +1 -1
- package/dist/utils.js +33 -35
- package/dist/utils.js.map +1 -1
- package/package.json +122 -116
- package/dist/helpers/format.js +0 -305
- package/dist/helpers/format.js.map +0 -1
- package/dist/helpers/format.spec.js +0 -224
- package/dist/helpers/format.spec.js.map +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
4
|
+
var _index = require("./index");
|
|
5
|
+
var _util = require("util");
|
|
6
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @licstart The following is the entire license notice for the JavaScript code in this file.
|
|
10
|
+
*
|
|
11
|
+
* Shared modules for microservices of Melinda rest api batch import system
|
|
12
|
+
*
|
|
13
|
+
* Copyright (C) 2022 University Of Helsinki (The National Library Of Finland)
|
|
14
|
+
*
|
|
15
|
+
* This file is part of melinda-rest-api-commons
|
|
16
|
+
*
|
|
17
|
+
* melinda-rest-api-commons program is free software: you can redistribute it and/or modify
|
|
18
|
+
* it under the terms of the GNU Affero General Public License as
|
|
19
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
20
|
+
* License, or (at your option) any later version.
|
|
21
|
+
*
|
|
22
|
+
* melinda-rest-api-commons is distributed in the hope that it will be useful,
|
|
23
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
24
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
25
|
+
* GNU Affero General Public License for more details.
|
|
26
|
+
*
|
|
27
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
28
|
+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
29
|
+
*
|
|
30
|
+
* @licend The above is the entire license notice
|
|
31
|
+
* for the JavaScript code in this file.
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
//import {expect} from 'chai';
|
|
36
|
+
|
|
37
|
+
const debug = (0, _debug.default)('@natlibfi/melinda-rest-api-commons:amqp:test');
|
|
38
|
+
//const debugData = debug.extend('data');
|
|
39
|
+
|
|
40
|
+
const setTimeoutPromise = (0, _util.promisify)(setTimeout);
|
|
41
|
+
describe('HealthCheck: amqpOperator should error if channel or connection fails', () => {
|
|
42
|
+
it('FIX: test healthCheck - this test does not really test anything, but error can be seen in debug!', async () => {
|
|
43
|
+
debug(`Testing amqp against a local AMQP instance - we'll need some kind of mockup for this`);
|
|
44
|
+
const amqpUrl = 'amqp://127.0.0.1:5672/';
|
|
45
|
+
const amqpOperator = await (0, _index.amqpFactory)(amqpUrl, true);
|
|
46
|
+
debug(`Connecting to ${amqpUrl}, with healthCheck running`);
|
|
47
|
+
await awaitTime(amqpOperator, false);
|
|
48
|
+
debug(`Testing healthCheckLoop for ${amqpOperator}`);
|
|
49
|
+
debug(`Waiting 0.5s and expecting healthCheckLoop to work`);
|
|
50
|
+
await awaitTime(amqpOperator, false);
|
|
51
|
+
debug(`Amqp Operator should have been alive.`);
|
|
52
|
+
debug(`Testing healthCheckLoop with closed channel`);
|
|
53
|
+
debug(`Waiting 0.5s and expecting healthCheckLoop to throw error`);
|
|
54
|
+
await awaitTime(amqpOperator, true);
|
|
55
|
+
debug(`Amqp Operator should have been errored.`);
|
|
56
|
+
debug(`Closing connection`);
|
|
57
|
+
await amqpOperator.closeConnection();
|
|
58
|
+
async function awaitTime(amqpOperator, close) {
|
|
59
|
+
if (close) {
|
|
60
|
+
await amqpOperator.closeChannel();
|
|
61
|
+
await setTimeoutPromise(500);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
await setTimeoutPromise(500);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=amqp.health.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amqp.health.spec.js","names":["debug","createDebugLogger","setTimeoutPromise","promisify","setTimeout","describe","it","amqpUrl","amqpOperator","amqpFactory","awaitTime","closeConnection","close","closeChannel"],"sources":["../src/amqp.health.spec.js"],"sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* Shared modules for microservices of Melinda rest api batch import system\n*\n* Copyright (C) 2022 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of melinda-rest-api-commons\n*\n* melinda-rest-api-commons program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Affero General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* melinda-rest-api-commons is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Affero General Public License for more details.\n*\n* You should have received a copy of the GNU Affero General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\n//import {expect} from 'chai';\nimport createDebugLogger from 'debug';\nimport {amqpFactory} from './index';\n\n\nconst debug = createDebugLogger('@natlibfi/melinda-rest-api-commons:amqp:test');\n//const debugData = debug.extend('data');\n\nimport {promisify} from 'util';\nconst setTimeoutPromise = promisify(setTimeout);\n\ndescribe('HealthCheck: amqpOperator should error if channel or connection fails', () => {\n\n it('FIX: test healthCheck - this test does not really test anything, but error can be seen in debug!', async () => {\n debug(`Testing amqp against a local AMQP instance - we'll need some kind of mockup for this`);\n const amqpUrl = 'amqp://127.0.0.1:5672/';\n\n const amqpOperator = await amqpFactory(amqpUrl, true);\n debug(`Connecting to ${amqpUrl}, with healthCheck running`);\n await awaitTime(amqpOperator, false);\n\n debug(`Testing healthCheckLoop for ${amqpOperator}`);\n debug(`Waiting 0.5s and expecting healthCheckLoop to work`);\n await awaitTime(amqpOperator, false);\n debug(`Amqp Operator should have been alive.`);\n\n debug(`Testing healthCheckLoop with closed channel`);\n debug(`Waiting 0.5s and expecting healthCheckLoop to throw error`);\n await awaitTime(amqpOperator, true);\n debug(`Amqp Operator should have been errored.`);\n\n debug(`Closing connection`);\n await amqpOperator.closeConnection();\n\n async function awaitTime(amqpOperator, close) {\n if (close) {\n await amqpOperator.closeChannel();\n await setTimeoutPromise(500);\n return;\n }\n\n await setTimeoutPromise(500);\n return;\n }\n\n });\n});\n\n"],"mappings":";;AA6BA;AACA;AAMA;AAA+B;AApC/B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAKA,MAAMA,KAAK,GAAG,IAAAC,cAAiB,EAAC,8CAA8C,CAAC;AAC/E;;AAGA,MAAMC,iBAAiB,GAAG,IAAAC,eAAS,EAACC,UAAU,CAAC;AAE/CC,QAAQ,CAAC,uEAAuE,EAAE,MAAM;EAEtFC,EAAE,CAAC,kGAAkG,EAAE,YAAY;IACjHN,KAAK,CAAE,sFAAqF,CAAC;IAC7F,MAAMO,OAAO,GAAG,wBAAwB;IAExC,MAAMC,YAAY,GAAG,MAAM,IAAAC,kBAAW,EAACF,OAAO,EAAE,IAAI,CAAC;IACrDP,KAAK,CAAE,iBAAgBO,OAAQ,4BAA2B,CAAC;IAC3D,MAAMG,SAAS,CAACF,YAAY,EAAE,KAAK,CAAC;IAEpCR,KAAK,CAAE,+BAA8BQ,YAAa,EAAC,CAAC;IACpDR,KAAK,CAAE,oDAAmD,CAAC;IAC3D,MAAMU,SAAS,CAACF,YAAY,EAAE,KAAK,CAAC;IACpCR,KAAK,CAAE,uCAAsC,CAAC;IAE9CA,KAAK,CAAE,6CAA4C,CAAC;IACpDA,KAAK,CAAE,2DAA0D,CAAC;IAClE,MAAMU,SAAS,CAACF,YAAY,EAAE,IAAI,CAAC;IACnCR,KAAK,CAAE,yCAAwC,CAAC;IAEhDA,KAAK,CAAE,oBAAmB,CAAC;IAC3B,MAAMQ,YAAY,CAACG,eAAe,EAAE;IAEpC,eAAeD,SAAS,CAACF,YAAY,EAAEI,KAAK,EAAE;MAC5C,IAAIA,KAAK,EAAE;QACT,MAAMJ,YAAY,CAACK,YAAY,EAAE;QACjC,MAAMX,iBAAiB,CAAC,GAAG,CAAC;QAC5B;MACF;MAEA,MAAMA,iBAAiB,CAAC,GAAG,CAAC;MAC5B;IACF;EAEF,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/amqp.js
CHANGED
|
@@ -4,32 +4,23 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = _default;
|
|
7
|
-
|
|
8
7
|
var _amqplib = _interopRequireDefault(require("amqplib"));
|
|
9
|
-
|
|
10
8
|
var _marcRecord = require("@natlibfi/marc-record");
|
|
11
|
-
|
|
12
9
|
var _melindaCommons = require("@natlibfi/melinda-commons");
|
|
13
|
-
|
|
14
10
|
var _melindaBackendCommons = require("@natlibfi/melinda-backend-commons");
|
|
15
|
-
|
|
11
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
16
12
|
var _constants = require("./constants");
|
|
17
|
-
|
|
18
|
-
var _utils = require("./utils");
|
|
19
|
-
|
|
20
13
|
var _util = require("util");
|
|
21
|
-
|
|
22
14
|
var _httpStatus = _interopRequireDefault(require("http-status"));
|
|
23
|
-
|
|
15
|
+
var _utils = require("./utils");
|
|
24
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
|
-
|
|
26
17
|
/**
|
|
27
18
|
*
|
|
28
19
|
* @licstart The following is the entire license notice for the JavaScript code in this file.
|
|
29
20
|
*
|
|
30
21
|
* Shared modules for microservices of Melinda rest api batch import system
|
|
31
22
|
*
|
|
32
|
-
* Copyright (C) 2020-
|
|
23
|
+
* Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)
|
|
33
24
|
*
|
|
34
25
|
* This file is part of melinda-rest-api-commons
|
|
35
26
|
*
|
|
@@ -50,10 +41,19 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
50
41
|
* for the JavaScript code in this file.
|
|
51
42
|
*
|
|
52
43
|
*/
|
|
53
|
-
|
|
44
|
+
|
|
45
|
+
async function _default(AMQP_URL, runHealthCheck = false) {
|
|
46
|
+
const logger = (0, _melindaBackendCommons.createLogger)();
|
|
47
|
+
const debug = (0, _debug.default)('@natlibfi/melinda-rest-api-commons:amqp');
|
|
48
|
+
const debugHC = debug.extend('HC');
|
|
49
|
+
const debugData = debug.extend('data');
|
|
50
|
+
debug(`Creating an AMQP operator to ${AMQP_URL}`);
|
|
54
51
|
const connection = await _amqplib.default.connect(AMQP_URL);
|
|
55
52
|
const channel = await connection.createChannel();
|
|
56
|
-
const
|
|
53
|
+
const healthCheckLoop = runHealthCheck ? healthCheck() : false;
|
|
54
|
+
debug(`Connection: ${connection}`);
|
|
55
|
+
debug(`Channel: ${channel}`);
|
|
56
|
+
debug(`HealthCheckLoop: ${healthCheckLoop ? 'Running health check' : 'Not running health check'}`);
|
|
57
57
|
return {
|
|
58
58
|
checkQueue,
|
|
59
59
|
consumeChunk,
|
|
@@ -62,82 +62,118 @@ async function _default(AMQP_URL) {
|
|
|
62
62
|
nackMessages,
|
|
63
63
|
sendToQueue,
|
|
64
64
|
removeQueue,
|
|
65
|
-
messagesToRecords
|
|
66
|
-
|
|
65
|
+
messagesToRecords,
|
|
66
|
+
closeChannel,
|
|
67
|
+
closeConnection
|
|
68
|
+
};
|
|
69
|
+
async function closeChannel() {
|
|
70
|
+
debug(`Closing channel`);
|
|
71
|
+
await channel.close();
|
|
72
|
+
debug(`Channel: ${channel}`);
|
|
73
|
+
}
|
|
74
|
+
async function closeConnection() {
|
|
75
|
+
debug(`Closing connection`);
|
|
76
|
+
await connection.close();
|
|
77
|
+
debug(`Connection: ${connection}`);
|
|
78
|
+
}
|
|
79
|
+
async function healthCheck(wait = false) {
|
|
80
|
+
const setTimeoutPromise = (0, _util.promisify)(setTimeout);
|
|
81
|
+
if (wait) {
|
|
82
|
+
await setTimeoutPromise(wait);
|
|
83
|
+
return healthCheck(false);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
debugHC(`Health checking amqp by asserting a queue`);
|
|
87
|
+
await channel.assertQueue('HEALTHCHECK', {
|
|
88
|
+
durable: false
|
|
89
|
+
});
|
|
90
|
+
debugHC(`Waiting 200ms before running healthCheck next`);
|
|
91
|
+
return healthCheck('200');
|
|
92
|
+
} catch (error) {
|
|
93
|
+
debugHC(`HealthCheck error ${JSON.stringify(error)}`);
|
|
94
|
+
handleAmqpErrors(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
67
97
|
|
|
98
|
+
// eslint-disable-next-line max-statements
|
|
68
99
|
async function checkQueue({
|
|
69
100
|
queue,
|
|
70
101
|
style = 'basic',
|
|
71
102
|
toRecord = true,
|
|
72
103
|
purge = false
|
|
73
104
|
}) {
|
|
74
|
-
|
|
75
|
-
|
|
105
|
+
debug(`checkQueue: ${queue}, Style: ${style}, toRecord: ${toRecord}, Purge: ${purge}`);
|
|
76
106
|
try {
|
|
77
107
|
errorUndefinedQueue(queue);
|
|
78
108
|
const channelInfo = await channel.assertQueue(queue, {
|
|
79
109
|
durable: true
|
|
80
110
|
});
|
|
81
|
-
|
|
82
111
|
if (purge) {
|
|
83
112
|
await purgeQueue(purge);
|
|
84
113
|
logger.verbose(`Queue ${queue} has purged ${channelInfo.messageCount} messages`);
|
|
114
|
+
debug(`Queue ${queue} has purged ${channelInfo.messageCount} messages`);
|
|
85
115
|
return checkQueue({
|
|
86
116
|
queue,
|
|
87
117
|
style
|
|
88
118
|
});
|
|
89
119
|
}
|
|
90
|
-
|
|
91
|
-
if (channelInfo.messageCount < 1) {
|
|
92
|
-
logger.silly(`checkQueue: ${channelInfo.messageCount} - ${queue} is empty`);
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
logger.silly(`Queue ${queue} has ${channelInfo.messageCount} messages`);
|
|
97
|
-
|
|
98
120
|
if (style === 'messages') {
|
|
99
121
|
return channelInfo.messageCount;
|
|
100
|
-
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Note: returns one message (+ record, of toRecord: true)
|
|
101
125
|
// note: if toRecord is false returns just plain message / false
|
|
102
126
|
// note: if toRecord is true returns {headers, records, messages} -object / false
|
|
103
127
|
// should this be more consistent?
|
|
104
|
-
|
|
105
|
-
|
|
106
128
|
if (style === 'one') {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
129
|
+
if (checkMessageCount(channelInfo)) {
|
|
130
|
+
return consumeOne(queue, toRecord);
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
111
134
|
|
|
135
|
+
// Note: returns a chunk of (100) messages (+ records, if toRecord: true)
|
|
136
|
+
// returns {headers, records, messages} object or {headers, messages} object depending on toRecord
|
|
112
137
|
if (style === 'basic') {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
138
|
+
if (checkMessageCount(channelInfo)) {
|
|
139
|
+
return consumeChunk(queue, toRecord);
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
116
143
|
|
|
117
|
-
|
|
144
|
+
// Defaults:
|
|
145
|
+
throw new _melindaCommons.Error(_httpStatus.default.INTERNAL_SERVER_ERROR, `CheckQueue does not recognize style ${style}`);
|
|
118
146
|
} catch (error) {
|
|
119
|
-
(
|
|
147
|
+
handleAmqpErrors(error);
|
|
148
|
+
}
|
|
149
|
+
function checkMessageCount(channelInfo) {
|
|
150
|
+
if (channelInfo.messageCount < 1) {
|
|
151
|
+
debug(`checkQueue: ${channelInfo.messageCount} - ${queue} is empty`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
debug(`Queue ${queue} has ${channelInfo.messageCount} messages`);
|
|
155
|
+
return true;
|
|
120
156
|
}
|
|
121
|
-
|
|
122
157
|
function purgeQueue(purge) {
|
|
158
|
+
debug(`Purging queue: ${queue}, purge: ${purge}`);
|
|
123
159
|
if (purge) {
|
|
124
160
|
return channel.purgeQueue(queue);
|
|
125
161
|
}
|
|
126
162
|
}
|
|
127
163
|
}
|
|
128
|
-
|
|
129
164
|
async function consumeChunk(queue, toRecord) {
|
|
130
|
-
|
|
131
|
-
|
|
165
|
+
debug(`Prepared to consumeChunk from queue: ${queue}`);
|
|
132
166
|
try {
|
|
133
167
|
await channel.assertQueue(queue, {
|
|
134
168
|
durable: true
|
|
135
|
-
});
|
|
169
|
+
});
|
|
136
170
|
|
|
171
|
+
// getData: next chunk (100) messages
|
|
137
172
|
const messages = await getData(queue);
|
|
138
|
-
const headers = getHeaderInfo(messages[0]);
|
|
139
|
-
logger.debug(`consumeChunk (${messages ? messages.length : '0'} from queue ${queue}) ${toRecord ? 'to records' : 'just messages'}`);
|
|
140
173
|
|
|
174
|
+
// Note: headers are from the first message of the chunk
|
|
175
|
+
const headers = getHeaderInfo(messages[0]);
|
|
176
|
+
debug(`consumeChunk (${messages ? messages.length : '0'} from queue ${queue}) ${toRecord ? 'to records' : 'just messages'}`);
|
|
141
177
|
if (toRecord) {
|
|
142
178
|
const records = await messagesToRecords(messages);
|
|
143
179
|
return {
|
|
@@ -146,33 +182,30 @@ async function _default(AMQP_URL) {
|
|
|
146
182
|
messages
|
|
147
183
|
};
|
|
148
184
|
}
|
|
149
|
-
|
|
150
185
|
return {
|
|
151
186
|
headers,
|
|
152
187
|
messages
|
|
153
188
|
};
|
|
154
189
|
} catch (error) {
|
|
155
|
-
(
|
|
190
|
+
handleAmqpErrors(error);
|
|
156
191
|
}
|
|
157
192
|
}
|
|
158
|
-
|
|
159
193
|
async function consumeOne(queue, toRecord) {
|
|
160
|
-
|
|
161
|
-
|
|
194
|
+
debug(`Prepared to consumeOne from queue: ${queue}`);
|
|
162
195
|
try {
|
|
163
196
|
await channel.assertQueue(queue, {
|
|
164
197
|
durable: true
|
|
165
|
-
});
|
|
198
|
+
});
|
|
166
199
|
|
|
200
|
+
// Returns false if 0 items in queue
|
|
167
201
|
const message = await channel.get(queue);
|
|
168
|
-
|
|
202
|
+
debugData(`Message: ${(0, _util.inspect)(message, {
|
|
169
203
|
colors: true,
|
|
170
204
|
maxArrayLength: 3,
|
|
171
205
|
depth: 3
|
|
172
|
-
})}`);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
206
|
+
})}`);
|
|
207
|
+
// Do not spam the logs
|
|
208
|
+
debug(`consumeOne from queue: ${queue} ${toRecord ? 'to records' : 'just the message'}`);
|
|
176
209
|
if (message) {
|
|
177
210
|
if (toRecord) {
|
|
178
211
|
const headers = getHeaderInfo(message);
|
|
@@ -183,127 +216,137 @@ async function _default(AMQP_URL) {
|
|
|
183
216
|
messages: [message]
|
|
184
217
|
};
|
|
185
218
|
}
|
|
186
|
-
|
|
187
219
|
return message;
|
|
188
220
|
}
|
|
189
|
-
|
|
190
221
|
return false;
|
|
191
222
|
} catch (error) {
|
|
192
|
-
(
|
|
223
|
+
handleAmqpErrors(error);
|
|
193
224
|
}
|
|
194
225
|
}
|
|
195
|
-
|
|
196
226
|
function ackMessages(messages) {
|
|
197
227
|
messages.forEach(message => {
|
|
198
|
-
|
|
228
|
+
debug(`Ack message ${message.properties.correlationId}`);
|
|
199
229
|
channel.ack(message);
|
|
200
230
|
});
|
|
201
231
|
}
|
|
202
|
-
|
|
203
232
|
function nackMessages(messages) {
|
|
204
233
|
messages.forEach(message => {
|
|
205
|
-
|
|
234
|
+
debug(`Nack message ${message.properties.correlationId}`);
|
|
206
235
|
channel.nack(message);
|
|
207
236
|
});
|
|
208
237
|
}
|
|
209
|
-
|
|
210
238
|
async function sendToQueue({
|
|
211
239
|
queue,
|
|
212
240
|
correlationId,
|
|
213
241
|
headers,
|
|
214
242
|
data
|
|
215
243
|
}) {
|
|
244
|
+
debug(`sendToQueue`);
|
|
245
|
+
// eslint-disable-next-line no-useless-catch
|
|
216
246
|
try {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
247
|
+
debug(`Queue ${queue}`);
|
|
248
|
+
debug(`CorrelationId ${correlationId}`);
|
|
249
|
+
debugData(`Data ${JSON.stringify(data)}`);
|
|
250
|
+
debug(`Headers ${JSON.stringify(headers)}`);
|
|
221
251
|
errorUndefinedQueue(queue);
|
|
252
|
+
debug(`Asserting queue: ${queue}`);
|
|
222
253
|
await channel.assertQueue(queue, {
|
|
223
254
|
durable: true
|
|
224
255
|
});
|
|
225
|
-
|
|
256
|
+
debug(`Actually sendToQueue: ${queue}`);
|
|
257
|
+
await channel.sendToQueue(queue, Buffer.from(JSON.stringify({
|
|
226
258
|
data
|
|
227
259
|
})), {
|
|
228
260
|
correlationId,
|
|
229
261
|
persistent: true,
|
|
230
262
|
headers
|
|
231
263
|
});
|
|
232
|
-
|
|
264
|
+
debug(`Send message for ${correlationId} to queue: ${queue}`);
|
|
233
265
|
} catch (error) {
|
|
234
|
-
|
|
266
|
+
const errorToThrow = error;
|
|
267
|
+
//if (error instanceof ApiError) {
|
|
268
|
+
// throw error;
|
|
269
|
+
//}
|
|
270
|
+
debug(`SendToQueue errored: ${JSON.stringify(error)}`);
|
|
271
|
+
handleAmqpErrors(errorToThrow);
|
|
235
272
|
}
|
|
236
273
|
}
|
|
237
|
-
|
|
238
274
|
async function removeQueue(queue) {
|
|
239
275
|
// deleteQueue borks the channel if the queue does not exist
|
|
240
276
|
// -> use throwaway tempChannel to avoid killing actual channel in use
|
|
241
277
|
// this might be doable also with assertQueue before deleteQueue
|
|
242
278
|
const tempChannel = await connection.createChannel();
|
|
243
279
|
logger.verbose(`Removing queue ${queue}.`);
|
|
280
|
+
debug(`Removing queue ${queue}.`);
|
|
244
281
|
await tempChannel.deleteQueue(queue);
|
|
245
|
-
|
|
246
282
|
if (tempChannel) {
|
|
247
|
-
// eslint-disable-line functional/no-conditional-statement
|
|
248
283
|
await tempChannel.close();
|
|
284
|
+
return;
|
|
249
285
|
}
|
|
250
|
-
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ----------------
|
|
251
290
|
// Helper functions
|
|
252
291
|
// ----------------
|
|
253
292
|
|
|
254
|
-
|
|
255
293
|
function messagesToRecords(messages) {
|
|
256
|
-
|
|
294
|
+
debug(`Parsing messages (${messages.length}) to records`);
|
|
257
295
|
return messages.map(message => {
|
|
258
296
|
const content = JSON.parse(message.content.toString());
|
|
297
|
+
// Should we have here validationOptions
|
|
259
298
|
return new _marcRecord.MarcRecord(content.data);
|
|
260
299
|
});
|
|
261
300
|
}
|
|
262
|
-
|
|
263
301
|
async function getData(queue) {
|
|
264
|
-
|
|
265
|
-
|
|
302
|
+
debug(`Getting queue data from ${queue}`);
|
|
266
303
|
try {
|
|
267
304
|
const {
|
|
268
305
|
messageCount
|
|
269
306
|
} = await channel.checkQueue(queue);
|
|
270
|
-
|
|
307
|
+
debug(`There is ${messageCount} messages in queue ${queue}`);
|
|
271
308
|
const messagesToGet = messageCount >= _constants.CHUNK_SIZE ? _constants.CHUNK_SIZE : messageCount;
|
|
272
|
-
|
|
309
|
+
debug(`Getting ${messagesToGet} messages from queue ${queue}`);
|
|
273
310
|
const messages = await pump(messagesToGet);
|
|
274
|
-
|
|
311
|
+
debug(`Returning ${messages.length} unique messages`);
|
|
275
312
|
return messages;
|
|
276
313
|
} catch (error) {
|
|
277
|
-
(
|
|
314
|
+
handleAmqpErrors(error);
|
|
278
315
|
}
|
|
279
|
-
|
|
280
316
|
async function pump(count, results = [], identifiers = []) {
|
|
281
317
|
if (count === 0) {
|
|
282
318
|
return results;
|
|
283
319
|
}
|
|
284
|
-
|
|
285
320
|
const message = await channel.get(queue);
|
|
286
321
|
const identifier = {
|
|
287
322
|
correlationId: message.properties.correlationId,
|
|
288
323
|
deliveryTag: message.fields.deliveryTag
|
|
289
|
-
};
|
|
290
|
-
|
|
324
|
+
};
|
|
325
|
+
// Filter not unique messages
|
|
291
326
|
if (identifiers.includes(identifier)) {
|
|
292
327
|
return pump(count - 1, results, identifiers);
|
|
293
328
|
}
|
|
294
|
-
|
|
295
329
|
return pump(count - 1, results.concat(message), identifiers.concat(identifier));
|
|
296
330
|
}
|
|
297
331
|
}
|
|
298
|
-
|
|
299
332
|
function getHeaderInfo(data) {
|
|
300
333
|
return data.properties.headers;
|
|
301
334
|
}
|
|
302
|
-
|
|
303
335
|
function errorUndefinedQueue(queue) {
|
|
304
336
|
if (queue === undefined || queue === '' || queue === null) {
|
|
305
337
|
throw new _melindaCommons.Error(_httpStatus.default.INTERNAL_SERVER_ERROR, `Undefined queue!`);
|
|
306
338
|
}
|
|
307
339
|
}
|
|
340
|
+
function handleAmqpErrors(error) {
|
|
341
|
+
const newError = error;
|
|
342
|
+
debug(`HandleAmqpErrors got an error: ${JSON.stringify(error)}`);
|
|
343
|
+
if (error instanceof _melindaCommons.Error) {
|
|
344
|
+
debug(`We have an ApiError`);
|
|
345
|
+
throw new _melindaCommons.Error(newError.status, newError.payload);
|
|
346
|
+
}
|
|
347
|
+
debug(`We have a non-ApiError`);
|
|
348
|
+
(0, _utils.logError)(error);
|
|
349
|
+
throw newError;
|
|
350
|
+
}
|
|
308
351
|
}
|
|
309
352
|
//# sourceMappingURL=amqp.js.map
|
package/dist/amqp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"amqp.js","names":["AMQP_URL","connection","amqplib","connect","channel","createChannel","logger","createLogger","checkQueue","consumeChunk","consumeOne","ackMessages","nackMessages","sendToQueue","removeQueue","messagesToRecords","queue","style","toRecord","purge","silly","errorUndefinedQueue","channelInfo","assertQueue","durable","purgeQueue","verbose","messageCount","ApiError","httpStatus","UNPROCESSABLE_ENTITY","error","logError","messages","getData","headers","getHeaderInfo","debug","length","records","message","get","inspect","colors","maxArrayLength","depth","forEach","properties","correlationId","ack","nack","data","JSON","stringify","Buffer","from","persistent","tempChannel","deleteQueue","close","map","content","parse","toString","MarcRecord","messagesToGet","CHUNK_SIZE","pump","count","results","identifiers","identifier","deliveryTag","fields","includes","concat","undefined","INTERNAL_SERVER_ERROR"],"sources":["../src/amqp.js"],"sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* Shared modules for microservices of Melinda rest api batch import system\n*\n* Copyright (C) 2020-2021 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of melinda-rest-api-commons\n*\n* melinda-rest-api-commons program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Affero General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* melinda-rest-api-commons is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Affero General Public License for more details.\n*\n* You should have received a copy of the GNU Affero General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport amqplib from 'amqplib';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {Error as ApiError} from '@natlibfi/melinda-commons';\nimport {createLogger} from '@natlibfi/melinda-backend-commons';\nimport {CHUNK_SIZE} from './constants';\nimport {logError} from './utils';\nimport {inspect} from 'util';\nimport httpStatus from 'http-status';\n\nexport default async function (AMQP_URL) {\n const connection = await amqplib.connect(AMQP_URL);\n const channel = await connection.createChannel();\n const logger = createLogger();\n\n return {checkQueue, consumeChunk, consumeOne, ackMessages, nackMessages, sendToQueue, removeQueue, messagesToRecords};\n\n // eslint-disable-next-line max-statements\n async function checkQueue({queue, style = 'basic', toRecord = true, purge = false}) {\n logger.silly(`checkQueue: ${queue}, Style: ${style}: toRecord: ${toRecord}, Purge: ${purge}`);\n\n try {\n\n errorUndefinedQueue(queue);\n const channelInfo = await channel.assertQueue(queue, {durable: true});\n\n if (purge) {\n await purgeQueue(purge);\n logger.verbose(`Queue ${queue} has purged ${channelInfo.messageCount} messages`);\n return checkQueue({queue, style});\n }\n\n if (channelInfo.messageCount < 1) {\n logger.silly(`checkQueue: ${channelInfo.messageCount} - ${queue} is empty`);\n return false;\n }\n logger.silly(`Queue ${queue} has ${channelInfo.messageCount} messages`);\n\n if (style === 'messages') {\n return channelInfo.messageCount;\n }\n\n // Note: returns one message (+ record, of toRecord: true)\n // note: if toRecord is false returns just plain message / false\n // note: if toRecord is true returns {headers, records, messages} -object / false\n // should this be more consistent?\n if (style === 'one') {\n return consumeOne(queue, toRecord);\n }\n\n // Note: returns a chunk of (100) messages (+ records, if toRecord: true)\n // returns {headers, records, messages} object or {headers, messages} object depending on toRecord\n if (style === 'basic') {\n return consumeChunk(queue, toRecord);\n }\n\n // Defaults:\n throw new ApiError(httpStatus.UNPROCESSABLE_ENTITY);\n } catch (error) {\n logError(error);\n }\n\n function purgeQueue(purge) {\n if (purge) {\n return channel.purgeQueue(queue);\n }\n }\n }\n\n async function consumeChunk(queue, toRecord) {\n logger.silly(`Prepared to consumeChunk from queue: ${queue}`);\n try {\n await channel.assertQueue(queue, {durable: true});\n\n // getData: next chunk (100) messages\n const messages = await getData(queue);\n const headers = getHeaderInfo(messages[0]);\n logger.debug(`consumeChunk (${messages ? messages.length : '0'} from queue ${queue}) ${toRecord ? 'to records' : 'just messages'}`);\n\n if (toRecord) {\n const records = await messagesToRecords(messages);\n return {headers, records, messages};\n }\n\n return {headers, messages};\n } catch (error) {\n logError(error);\n }\n }\n\n async function consumeOne(queue, toRecord) {\n logger.silly(`Prepared to consumeOne from queue: ${queue}`);\n try {\n await channel.assertQueue(queue, {durable: true});\n\n // Returns false if 0 items in queue\n const message = await channel.get(queue);\n\n logger.silly(`Message: ${inspect(message, {colors: true, maxArrayLength: 3, depth: 3})}`);\n // Do not spam the logs\n logger.debug(`consumeOne from queue: ${queue} ${toRecord ? 'to records' : 'just the message'}`);\n\n if (message) {\n if (toRecord) {\n const headers = getHeaderInfo(message);\n const records = messagesToRecords([message]);\n return {headers, records, messages: [message]};\n }\n\n return message;\n }\n\n return false;\n } catch (error) {\n logError(error);\n }\n }\n\n function ackMessages(messages) {\n messages.forEach(message => {\n logger.silly(`Ack message ${message.properties.correlationId}`);\n channel.ack(message);\n });\n }\n\n function nackMessages(messages) {\n messages.forEach(message => {\n logger.silly(`Nack message ${message.properties.correlationId}`);\n channel.nack(message);\n });\n }\n\n async function sendToQueue({queue, correlationId, headers, data}) {\n try {\n logger.silly(`Queue ${queue}`);\n logger.silly(`CorrelationId ${correlationId}`);\n logger.silly(`Data ${JSON.stringify(data)}`);\n logger.silly(`Headers ${JSON.stringify(headers)}`);\n\n errorUndefinedQueue(queue);\n await channel.assertQueue(queue, {durable: true});\n\n channel.sendToQueue(\n queue,\n Buffer.from(JSON.stringify({data})),\n {\n correlationId,\n persistent: true,\n headers\n }\n );\n logger.silly(`Send message for ${correlationId} to queue: ${queue}`);\n } catch (error) {\n logError(error);\n }\n }\n\n async function removeQueue(queue) {\n // deleteQueue borks the channel if the queue does not exist\n // -> use throwaway tempChannel to avoid killing actual channel in use\n // this might be doable also with assertQueue before deleteQueue\n const tempChannel = await connection.createChannel();\n logger.verbose(`Removing queue ${queue}.`);\n await tempChannel.deleteQueue(queue);\n if (tempChannel) { // eslint-disable-line functional/no-conditional-statement\n await tempChannel.close();\n }\n }\n\n // ----------------\n // Helper functions\n // ----------------\n\n function messagesToRecords(messages) {\n logger.debug(`Parsing messages (${messages.length}) to records`);\n\n return messages.map(message => {\n const content = JSON.parse(message.content.toString());\n return new MarcRecord(content.data);\n });\n }\n\n async function getData(queue) {\n logger.debug(`Getting queue data from ${queue}`);\n try {\n const {messageCount} = await channel.checkQueue(queue);\n logger.silly(`There is ${messageCount} messages in queue ${queue}`);\n const messagesToGet = messageCount >= CHUNK_SIZE ? CHUNK_SIZE : messageCount;\n logger.silly(`Getting ${messagesToGet} messages from queue ${queue}`);\n\n const messages = await pump(messagesToGet);\n\n logger.debug(`Returning ${messages.length} unique messages`);\n\n return messages;\n } catch (error) {\n logError(error);\n }\n\n async function pump(count, results = [], identifiers = []) {\n if (count === 0) {\n return results;\n }\n\n const message = await channel.get(queue);\n const identifier = {\n correlationId: message.properties.correlationId,\n deliveryTag: message.fields.deliveryTag\n };\n // Filter not unique messages\n if (identifiers.includes(identifier)) {\n return pump(count - 1, results, identifiers);\n }\n\n return pump(count - 1, results.concat(message), identifiers.concat(identifier));\n }\n }\n\n function getHeaderInfo(data) {\n return data.properties.headers;\n }\n\n function errorUndefinedQueue(queue) {\n if (queue === undefined || queue === '' || queue === null) {\n throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Undefined queue!`);\n }\n }\n}\n\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"amqp.js","names":["AMQP_URL","runHealthCheck","logger","createLogger","debug","createDebugLogger","debugHC","extend","debugData","connection","amqplib","connect","channel","createChannel","healthCheckLoop","healthCheck","checkQueue","consumeChunk","consumeOne","ackMessages","nackMessages","sendToQueue","removeQueue","messagesToRecords","closeChannel","closeConnection","close","wait","setTimeoutPromise","promisify","setTimeout","assertQueue","durable","error","JSON","stringify","handleAmqpErrors","queue","style","toRecord","purge","errorUndefinedQueue","channelInfo","purgeQueue","verbose","messageCount","checkMessageCount","ApiError","httpStatus","INTERNAL_SERVER_ERROR","messages","getData","headers","getHeaderInfo","length","records","message","get","inspect","colors","maxArrayLength","depth","forEach","properties","correlationId","ack","nack","data","Buffer","from","persistent","errorToThrow","tempChannel","deleteQueue","map","content","parse","toString","MarcRecord","messagesToGet","CHUNK_SIZE","pump","count","results","identifiers","identifier","deliveryTag","fields","includes","concat","undefined","newError","status","payload","logError"],"sources":["../src/amqp.js"],"sourcesContent":["/**\n*\n* @licstart The following is the entire license notice for the JavaScript code in this file.\n*\n* Shared modules for microservices of Melinda rest api batch import system\n*\n* Copyright (C) 2020-2022 University Of Helsinki (The National Library Of Finland)\n*\n* This file is part of melinda-rest-api-commons\n*\n* melinda-rest-api-commons program is free software: you can redistribute it and/or modify\n* it under the terms of the GNU Affero General Public License as\n* published by the Free Software Foundation, either version 3 of the\n* License, or (at your option) any later version.\n*\n* melinda-rest-api-commons is distributed in the hope that it will be useful,\n* but WITHOUT ANY WARRANTY; without even the implied warranty of\n* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n* GNU Affero General Public License for more details.\n*\n* You should have received a copy of the GNU Affero General Public License\n* along with this program. If not, see <http://www.gnu.org/licenses/>.\n*\n* @licend The above is the entire license notice\n* for the JavaScript code in this file.\n*\n*/\n\nimport amqplib from 'amqplib';\nimport {MarcRecord} from '@natlibfi/marc-record';\nimport {Error as ApiError} from '@natlibfi/melinda-commons';\nimport {createLogger} from '@natlibfi/melinda-backend-commons';\nimport createDebugLogger from 'debug';\nimport {CHUNK_SIZE} from './constants';\nimport {promisify, inspect} from 'util';\nimport httpStatus from 'http-status';\nimport {logError} from './utils';\n\nexport default async function (AMQP_URL, runHealthCheck = false) {\n const logger = createLogger();\n const debug = createDebugLogger('@natlibfi/melinda-rest-api-commons:amqp');\n const debugHC = debug.extend('HC');\n const debugData = debug.extend('data');\n\n debug(`Creating an AMQP operator to ${AMQP_URL}`);\n const connection = await amqplib.connect(AMQP_URL);\n const channel = await connection.createChannel();\n\n const healthCheckLoop = runHealthCheck ? healthCheck() : false;\n\n debug(`Connection: ${connection}`);\n debug(`Channel: ${channel}`);\n debug(`HealthCheckLoop: ${healthCheckLoop ? 'Running health check' : 'Not running health check'}`);\n\n return {checkQueue, consumeChunk, consumeOne, ackMessages, nackMessages, sendToQueue, removeQueue, messagesToRecords, closeChannel, closeConnection};\n\n async function closeChannel() {\n debug(`Closing channel`);\n await channel.close();\n debug(`Channel: ${channel}`);\n }\n\n async function closeConnection() {\n debug(`Closing connection`);\n await connection.close();\n debug(`Connection: ${connection}`);\n }\n\n async function healthCheck(wait = false) {\n const setTimeoutPromise = promisify(setTimeout);\n if (wait) {\n await setTimeoutPromise(wait);\n return healthCheck(false);\n }\n\n try {\n debugHC(`Health checking amqp by asserting a queue`);\n await channel.assertQueue('HEALTHCHECK', {durable: false});\n debugHC(`Waiting 200ms before running healthCheck next`);\n return healthCheck('200');\n } catch (error) {\n debugHC(`HealthCheck error ${JSON.stringify(error)}`);\n handleAmqpErrors(error);\n }\n }\n\n // eslint-disable-next-line max-statements\n async function checkQueue({queue, style = 'basic', toRecord = true, purge = false}) {\n debug(`checkQueue: ${queue}, Style: ${style}, toRecord: ${toRecord}, Purge: ${purge}`);\n\n try {\n\n errorUndefinedQueue(queue);\n const channelInfo = await channel.assertQueue(queue, {durable: true});\n\n if (purge) {\n await purgeQueue(purge);\n logger.verbose(`Queue ${queue} has purged ${channelInfo.messageCount} messages`);\n debug(`Queue ${queue} has purged ${channelInfo.messageCount} messages`);\n return checkQueue({queue, style});\n }\n\n if (style === 'messages') {\n return channelInfo.messageCount;\n }\n\n\n // Note: returns one message (+ record, of toRecord: true)\n // note: if toRecord is false returns just plain message / false\n // note: if toRecord is true returns {headers, records, messages} -object / false\n // should this be more consistent?\n if (style === 'one') {\n if (checkMessageCount(channelInfo)) {\n return consumeOne(queue, toRecord);\n }\n return false;\n }\n\n // Note: returns a chunk of (100) messages (+ records, if toRecord: true)\n // returns {headers, records, messages} object or {headers, messages} object depending on toRecord\n if (style === 'basic') {\n if (checkMessageCount(channelInfo)) {\n return consumeChunk(queue, toRecord);\n }\n return false;\n }\n\n // Defaults:\n throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `CheckQueue does not recognize style ${style}`);\n } catch (error) {\n handleAmqpErrors(error);\n }\n\n function checkMessageCount(channelInfo) {\n if (channelInfo.messageCount < 1) {\n debug(`checkQueue: ${channelInfo.messageCount} - ${queue} is empty`);\n return false;\n }\n debug(`Queue ${queue} has ${channelInfo.messageCount} messages`);\n return true;\n }\n\n function purgeQueue(purge) {\n debug(`Purging queue: ${queue}, purge: ${purge}`);\n if (purge) {\n return channel.purgeQueue(queue);\n }\n }\n }\n\n async function consumeChunk(queue, toRecord) {\n debug(`Prepared to consumeChunk from queue: ${queue}`);\n try {\n await channel.assertQueue(queue, {durable: true});\n\n // getData: next chunk (100) messages\n const messages = await getData(queue);\n\n // Note: headers are from the first message of the chunk\n const headers = getHeaderInfo(messages[0]);\n debug(`consumeChunk (${messages ? messages.length : '0'} from queue ${queue}) ${toRecord ? 'to records' : 'just messages'}`);\n\n if (toRecord) {\n const records = await messagesToRecords(messages);\n return {headers, records, messages};\n }\n\n return {headers, messages};\n } catch (error) {\n handleAmqpErrors(error);\n }\n }\n\n async function consumeOne(queue, toRecord) {\n debug(`Prepared to consumeOne from queue: ${queue}`);\n try {\n await channel.assertQueue(queue, {durable: true});\n\n // Returns false if 0 items in queue\n const message = await channel.get(queue);\n\n debugData(`Message: ${inspect(message, {colors: true, maxArrayLength: 3, depth: 3})}`);\n // Do not spam the logs\n debug(`consumeOne from queue: ${queue} ${toRecord ? 'to records' : 'just the message'}`);\n\n if (message) {\n if (toRecord) {\n const headers = getHeaderInfo(message);\n const records = messagesToRecords([message]);\n return {headers, records, messages: [message]};\n }\n\n return message;\n }\n\n return false;\n } catch (error) {\n handleAmqpErrors(error);\n }\n }\n\n function ackMessages(messages) {\n messages.forEach(message => {\n debug(`Ack message ${message.properties.correlationId}`);\n channel.ack(message);\n });\n }\n\n function nackMessages(messages) {\n messages.forEach(message => {\n debug(`Nack message ${message.properties.correlationId}`);\n channel.nack(message);\n });\n }\n\n async function sendToQueue({queue, correlationId, headers, data}) {\n debug(`sendToQueue`);\n // eslint-disable-next-line no-useless-catch\n try {\n debug(`Queue ${queue}`);\n debug(`CorrelationId ${correlationId}`);\n debugData(`Data ${JSON.stringify(data)}`);\n debug(`Headers ${JSON.stringify(headers)}`);\n\n errorUndefinedQueue(queue);\n\n debug(`Asserting queue: ${queue}`);\n await channel.assertQueue(queue, {durable: true});\n\n debug(`Actually sendToQueue: ${queue}`);\n await channel.sendToQueue(\n queue,\n Buffer.from(JSON.stringify({data})),\n {\n correlationId,\n persistent: true,\n headers\n }\n );\n debug(`Send message for ${correlationId} to queue: ${queue}`);\n\n\n } catch (error) {\n const errorToThrow = error;\n //if (error instanceof ApiError) {\n // throw error;\n //}\n debug(`SendToQueue errored: ${JSON.stringify(error)}`);\n handleAmqpErrors(errorToThrow);\n }\n }\n\n async function removeQueue(queue) {\n // deleteQueue borks the channel if the queue does not exist\n // -> use throwaway tempChannel to avoid killing actual channel in use\n // this might be doable also with assertQueue before deleteQueue\n const tempChannel = await connection.createChannel();\n logger.verbose(`Removing queue ${queue}.`);\n debug(`Removing queue ${queue}.`);\n await tempChannel.deleteQueue(queue);\n\n if (tempChannel) {\n await tempChannel.close();\n return;\n }\n\n return;\n }\n\n // ----------------\n // Helper functions\n // ----------------\n\n function messagesToRecords(messages) {\n debug(`Parsing messages (${messages.length}) to records`);\n\n return messages.map(message => {\n const content = JSON.parse(message.content.toString());\n // Should we have here validationOptions\n return new MarcRecord(content.data);\n });\n }\n\n async function getData(queue) {\n debug(`Getting queue data from ${queue}`);\n try {\n const {messageCount} = await channel.checkQueue(queue);\n debug(`There is ${messageCount} messages in queue ${queue}`);\n const messagesToGet = messageCount >= CHUNK_SIZE ? CHUNK_SIZE : messageCount;\n debug(`Getting ${messagesToGet} messages from queue ${queue}`);\n\n const messages = await pump(messagesToGet);\n\n debug(`Returning ${messages.length} unique messages`);\n\n return messages;\n } catch (error) {\n handleAmqpErrors(error);\n }\n\n async function pump(count, results = [], identifiers = []) {\n if (count === 0) {\n return results;\n }\n\n const message = await channel.get(queue);\n const identifier = {\n correlationId: message.properties.correlationId,\n deliveryTag: message.fields.deliveryTag\n };\n // Filter not unique messages\n if (identifiers.includes(identifier)) {\n return pump(count - 1, results, identifiers);\n }\n\n return pump(count - 1, results.concat(message), identifiers.concat(identifier));\n }\n }\n\n function getHeaderInfo(data) {\n return data.properties.headers;\n }\n\n function errorUndefinedQueue(queue) {\n if (queue === undefined || queue === '' || queue === null) {\n throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, `Undefined queue!`);\n }\n }\n\n function handleAmqpErrors(error) {\n const newError = error;\n debug(`HandleAmqpErrors got an error: ${JSON.stringify(error)}`);\n if (error instanceof ApiError) {\n debug(`We have an ApiError`);\n throw new ApiError(newError.status, newError.payload);\n }\n debug(`We have a non-ApiError`);\n logError(error);\n throw newError;\n }\n}\n\n\n"],"mappings":"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"}
|