@meltwater/conversations-api-services 1.0.40 → 1.0.41

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.
@@ -3,14 +3,25 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.IRClient = void 0;
6
+ exports.addRevisionsToDocuments = addRevisionsToDocuments;
7
+ exports.default = void 0;
8
+ exports.markAsComplete = markAsComplete;
9
+ exports.markAsHasDMReply = markAsHasDMReply;
10
+ exports.markAsToDo = markAsToDo;
7
11
  var _uuid = require("uuid");
8
12
  var _superagent = _interopRequireDefault(require("superagent"));
9
13
  var _logger = _interopRequireDefault(require("../../lib/logger.js"));
10
14
  var _configuration = _interopRequireDefault(require("../../lib/configuration.js"));
11
- var _metricsHelper = require("../../lib/metrics.helper.js");
12
15
  var _loggerHelpers = require("../../lib/logger.helpers.js");
13
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
+ const searchServicesUrl = _configuration.default.get('SEARCH_SERVICE_URL');
18
+ const searchServicesKey = process.env.IR_API_KEY;
19
+ const documentRevisionsServiceUrl = _configuration.default.get('DOCUMENT_REVISIONS_SERVICE_URL');
20
+ const documentServiceUrl = _configuration.default.get('DOCUMENT_SERVICE_URL');
21
+ const documentServiceKey = process.env.IR_API_KEY;
22
+ const exportApiUrl = _configuration.default.get('EXPORT_API_URL');
23
+ const exportApiKey = process.env.IR_API_KEY;
24
+ const chunkSize = process.env.REVISION_CHUNK_SIZE || 200;
14
25
  class IRClient {
15
26
  RETRY_COUNT = 50;
16
27
  constructor(context) {
@@ -25,47 +36,14 @@ class IRClient {
25
36
  this.traceId = traceId;
26
37
  this.metrics = metrics;
27
38
  }
28
- async init() {
29
- let irApiKey = _configuration.default.get('IR_API_KEY');
30
- this.searchServicesUrl = _configuration.default.get('SEARCH_SERVICE_URL');
31
- this.searchServicesKey = irApiKey;
32
- this.documentRevisionsServiceUrl = _configuration.default.get('DOCUMENT_REVISIONS_SERVICE_URL');
33
- this.documentServiceUrl = _configuration.default.get('DOCUMENT_SERVICE_URL');
34
- this.documentServiceKey = irApiKey;
35
- this.exportApiUrl = _configuration.default.get('EXPORT_API_URL');
36
- this.exportApiKey = irApiKey;
37
- }
38
39
  async addRevisionsToDocuments(operations) {
39
- await this.init();
40
- const wait = 100;
41
- const url = `${this.documentRevisionsServiceUrl}/${this.company._id}/`;
42
- const loggerChild = _logger.default.child({
43
- [_loggerHelpers.MeltwaterAttributes.COMPANYID]: this.company._id
44
- });
45
- let result;
46
- try {
47
- result = await _superagent.default.patch(url).query({
48
- wait,
49
- apikey: this.documentServiceKey
50
- }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
51
- } catch (error) {
52
- loggerChild.error(`Failed adding a revisions with operations`, error, {
53
- url,
54
- [_loggerHelpers.MeltwaterAttributes.COMPANYID]: this.company._id,
55
- operations: JSON.stringify(operations)
56
- });
57
- return;
58
- }
59
- loggerChild.info(`Finished adding revision operations`, {
60
- operations: JSON.stringify(operations)
61
- });
62
- return result;
40
+ return await addRevisionsToDocuments(operations, this.company._id, _logger.default);
63
41
  }
64
42
  async modifyDocuments(operations) {
65
43
  let retryAttempt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
66
44
  await this.init();
67
45
  const wait = 100;
68
- const url = `${this.documentServiceUrl}/`;
46
+ const url = `${documentServiceUrl}/`;
69
47
  const loggerChild = _logger.default.child({
70
48
  [_loggerHelpers.MeltwaterAttributes.COMPANYID]: this.company._id
71
49
  });
@@ -73,7 +51,7 @@ class IRClient {
73
51
  try {
74
52
  result = await _superagent.default.patch(url).query({
75
53
  wait,
76
- apikey: this.documentServiceKey
54
+ apikey: documentServiceKey
77
55
  }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
78
56
  } catch (err) {
79
57
  retryAttempt++;
@@ -94,7 +72,7 @@ class IRClient {
94
72
  let retryAttempt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
95
73
  await this.init();
96
74
  try {
97
- const uri = `${this.documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${this.documentServiceKey}`;
75
+ const uri = `${documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${documentServiceKey}`;
98
76
  const metric = this.metrics.count(metricNames.irGetByDocumentId);
99
77
  const result = await _superagent.default.get(uri);
100
78
  this.metrics.finishOperation(metricNames.irGetByDocumentId, metric);
@@ -135,8 +113,8 @@ class IRClient {
135
113
  await this.init();
136
114
  const traceId = this.generateTraceId();
137
115
  try {
138
- let uri = `${this.exportApiUrl}?apikey=${this.exportApiKey}`;
139
- _logger.default.info(`Starting call to : ${this.exportApiUrl}`, {
116
+ let uri = `${exportApiUrl}?apikey=${exportApiKey}`;
117
+ _logger.default.info(`Starting call to : ${exportApiUrl}`, {
140
118
  rune
141
119
  });
142
120
  this.addCompanyToRuneForHorace(rune);
@@ -155,7 +133,7 @@ class IRClient {
155
133
  await this.init();
156
134
  const traceId = this.generateTraceId();
157
135
  try {
158
- let uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
136
+ let uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
159
137
 
160
138
  // todo: remove changing rune searchResults size
161
139
  // todo: remove reduce ( dedupes and trims to original rune limit )
@@ -224,7 +202,7 @@ class IRClient {
224
202
  async executeSearch(rune) {
225
203
  await this.init();
226
204
  this.addCompanyToRuneForHorace(rune);
227
- const uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
205
+ const uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
228
206
  return await _superagent.default.post(uri).send(rune);
229
207
  }
230
208
  addCompanyToRuneForHorace(rune) {
@@ -239,4 +217,105 @@ class IRClient {
239
217
  return `engage_${this.company._id}_${this.traceId}_${(0, _uuid.v4)()}`;
240
218
  }
241
219
  }
242
- exports.IRClient = IRClient;
220
+ exports.default = IRClient;
221
+ async function addRevisionsToDocuments(operations, companyId, logger) {
222
+ const url = `${documentRevisionsServiceUrl}/${companyId}/`;
223
+ let result;
224
+ try {
225
+ result = await _superagent.default.patch(url).query({
226
+ apikey: documentServiceKey
227
+ }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
228
+ } catch (error) {
229
+ (0, _loggerHelpers.loggerError)(logger, `Failed adding a revisions with operations`, error, {
230
+ url,
231
+ [_loggerHelpers.MeltwaterAttributes.COMPANYID]: companyId,
232
+ operations: JSON.stringify(operations)
233
+ });
234
+ return;
235
+ }
236
+ return result;
237
+ }
238
+ async function markAsComplete(messageIds, companyId, logger) {
239
+ return reviseDocumentHelper(messageIds, 'markAsComplete', companyId, logger);
240
+ }
241
+ async function markAsHasDMReply(messageIds, companyId, logger) {
242
+ return reviseDocumentHelper(messageIds, 'markAsHasDMReply', companyId, logger);
243
+ }
244
+ async function markAsToDo(messageIds, companyId, logger) {
245
+ return reviseDocumentHelper(messageIds, 'markAsToDo', companyId, logger);
246
+ }
247
+ async function reviseDocumentHelper(messageIds, revisionType, companyId, logger) {
248
+ const messageIdsChunks = chunkArray(messageIds, chunkSize);
249
+ const revisionPromises = [];
250
+ for (const messageIdChunk of messageIdsChunks) {
251
+ const operations = {};
252
+ // build operation list for each document
253
+ switch (revisionType) {
254
+ case "markAsComplete":
255
+ messageIdChunk.forEach(message => {
256
+ operations[message.documentId] = [{
257
+ operation: 'addToSet',
258
+ fieldPath: 'metaData.applicationTags',
259
+ value: [generateCompanyAppDataKey({
260
+ companyId: companyId,
261
+ key: `${message.searchId || `credential:${message.credentialId}`}:status`,
262
+ value: 'complete'
263
+ })]
264
+ }];
265
+ });
266
+ break;
267
+ case "markAsHasDMReply":
268
+ messageIdChunk.forEach(message => {
269
+ operations[message.documentId] = [{
270
+ operation: 'addToSet',
271
+ fieldPath: 'metaData.applicationTags',
272
+ value: [generateCompanyAppDataKey({
273
+ companyId: companyId,
274
+ key: `${message.searchId || `credential:${message.credentialId}`}:hasBrandReply`,
275
+ value: 'true'
276
+ })]
277
+ }];
278
+ });
279
+ break;
280
+ case "markAsToDo":
281
+ messageIdChunk.forEach(message => {
282
+ operations[message.documentId] = [{
283
+ operation: 'removeFromSet',
284
+ fieldPath: 'metaData.applicationTags',
285
+ value: [generateCompanyAppDataKey({
286
+ companyId: companyId,
287
+ key: `${message.searchId || `credential:${message.credentialId}`}:status`,
288
+ value: `complete`
289
+ })]
290
+ }];
291
+ });
292
+ break;
293
+ }
294
+ try {
295
+ (0, _loggerHelpers.loggerInfo)(logger, `Sending revision for ${revisionType} for companyId: ${companyId}`, {
296
+ operations: JSON.stringify(operations)
297
+ });
298
+ revisionPromises.push(addRevisionsToDocuments(operations, companyId, logger));
299
+ } catch (error) {
300
+ (0, _loggerHelpers.loggerError)(logger, `Error marking as ${revisionType} in IR for ${messageIds.length} document(s). Unable to save revision.`, error, {
301
+ messageIds
302
+ });
303
+ }
304
+ }
305
+ return await Promise.all(revisionPromises);
306
+ }
307
+ function chunkArray(array, size) {
308
+ return array.reduce((chunks, item, index) => {
309
+ const chunkIndex = Math.floor(index / size);
310
+ chunks[chunkIndex] = (chunks[chunkIndex] || []).concat(item);
311
+ return chunks;
312
+ }, []);
313
+ }
314
+ function generateCompanyAppDataKey(_ref) {
315
+ let {
316
+ companyId,
317
+ key,
318
+ value
319
+ } = _ref;
320
+ return `conversations:${companyId}:${key}=${value}`;
321
+ }
@@ -28,11 +28,7 @@ var _crypto = _interopRequireDefault(require("crypto"));
28
28
  var _axios = _interopRequireDefault(require("axios"));
29
29
  var _fs = _interopRequireDefault(require("fs"));
30
30
  var _socialTwit = _interopRequireDefault(require("@meltwater/social-twit"));
31
- var _url = require("url");
32
- var _path = require("path");
33
31
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
34
- const _filename = (0, _url.fileURLToPath)(import.meta.url);
35
- const _dirname = (0, _path.dirname)(_filename);
36
32
  function addOAuthToToken(token, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET) {
37
33
  if (!token.oauth) {
38
34
  token.oauth = (0, _oauth.default)({
@@ -534,8 +530,8 @@ async function downloadImage(url, fileName) {
534
530
  }
535
531
 
536
532
  // local
537
- function generateFilePath(mimeType) {
538
- let dir = `${_dirname}/temporaryTwitterMedia`;
533
+ function generateFilePath(mimeType, dirname) {
534
+ let dir = `${dirname}/temporaryTwitterMedia`;
539
535
  if (!_fs.default.existsSync(dir)) {
540
536
  _fs.default.mkdirSync(dir);
541
537
  }
@@ -549,7 +545,7 @@ function generateFilePath(mimeType) {
549
545
  // local
550
546
  async function uploadMedia(attachment, token, discussionType, logger) {
551
547
  return new Promise(async (resolve, reject) => {
552
- let filePath = generateFilePath(attachment.mimeType);
548
+ let filePath = generateFilePath(attachment.mimeType, attachment.__dirname);
553
549
  await downloadImage(attachment.url, filePath);
554
550
  const T = new _socialTwit.default({
555
551
  consumer_key: token.consumer_key,
@@ -14,7 +14,8 @@ var _featureToggleApiClient = require("./http/featureToggleApi.client.js");
14
14
  var _identityServicesClient = require("./http/identityServices.client.js");
15
15
  var _instagramApiClient = require("./http/instagramApi.client.js");
16
16
  var _InstagramVideoClient = require("./http/InstagramVideoClient.js");
17
- var _irClient = require("./http/ir.client.js");
17
+ var _irClient = _interopRequireWildcard(require("./http/ir.client.js"));
18
+ var IRClientHelpers = _irClient;
18
19
  var _linkedInApiClient = require("./http/linkedInApi.client.js");
19
20
  var _tiktokApiClient = require("./http/tiktokApi.client.js");
20
21
  var _masfClient = require("./http/masf.client.js");
@@ -59,11 +60,12 @@ var _default = exports.default = {
59
60
  IdentityServicesClient: _identityServicesClient.IdentityServicesClient,
60
61
  InstagramApiClient: _instagramApiClient.InstagramApiClient,
61
62
  InstagramVideoClient: _InstagramVideoClient.InstagramVideoClient,
62
- IRClient: _irClient.IRClient,
63
+ IRClient: _irClient.default,
63
64
  LinkedInApiClient: _linkedInApiClient.LinkedInApiClient,
64
65
  TikTokApiClient: _tiktokApiClient.TikTokApiClient,
65
66
  MasfClient: _masfClient.MasfClient,
66
67
  WarpZoneApiClient: _WarpZoneApiClient.WarpZoneApiClient,
67
68
  DocumentHelperFunctions,
68
- LinkedInHelpers
69
+ LinkedInHelpers,
70
+ IRClientHelpers
69
71
  };
@@ -2,9 +2,16 @@ import { v4 } from 'uuid';
2
2
  import superagent from 'superagent';
3
3
  import logger from '../../lib/logger.js';
4
4
  import configuration from '../../lib/configuration.js';
5
- import { metricN } from '../../lib/metrics.helper.js';
6
- import { MeltwaterAttributes } from '../../lib/logger.helpers.js';
7
- export class IRClient {
5
+ import { loggerError, loggerInfo, MeltwaterAttributes } from '../../lib/logger.helpers.js';
6
+ const searchServicesUrl = configuration.get('SEARCH_SERVICE_URL');
7
+ const searchServicesKey = process.env.IR_API_KEY;
8
+ const documentRevisionsServiceUrl = configuration.get('DOCUMENT_REVISIONS_SERVICE_URL');
9
+ const documentServiceUrl = configuration.get('DOCUMENT_SERVICE_URL');
10
+ const documentServiceKey = process.env.IR_API_KEY;
11
+ const exportApiUrl = configuration.get('EXPORT_API_URL');
12
+ const exportApiKey = process.env.IR_API_KEY;
13
+ const chunkSize = process.env.REVISION_CHUNK_SIZE || 200;
14
+ export default class IRClient {
8
15
  RETRY_COUNT = 50;
9
16
  constructor(context) {
10
17
  const {
@@ -18,47 +25,14 @@ export class IRClient {
18
25
  this.traceId = traceId;
19
26
  this.metrics = metrics;
20
27
  }
21
- async init() {
22
- let irApiKey = configuration.get('IR_API_KEY');
23
- this.searchServicesUrl = configuration.get('SEARCH_SERVICE_URL');
24
- this.searchServicesKey = irApiKey;
25
- this.documentRevisionsServiceUrl = configuration.get('DOCUMENT_REVISIONS_SERVICE_URL');
26
- this.documentServiceUrl = configuration.get('DOCUMENT_SERVICE_URL');
27
- this.documentServiceKey = irApiKey;
28
- this.exportApiUrl = configuration.get('EXPORT_API_URL');
29
- this.exportApiKey = irApiKey;
30
- }
31
28
  async addRevisionsToDocuments(operations) {
32
- await this.init();
33
- const wait = 100;
34
- const url = `${this.documentRevisionsServiceUrl}/${this.company._id}/`;
35
- const loggerChild = logger.child({
36
- [MeltwaterAttributes.COMPANYID]: this.company._id
37
- });
38
- let result;
39
- try {
40
- result = await superagent.patch(url).query({
41
- wait,
42
- apikey: this.documentServiceKey
43
- }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
44
- } catch (error) {
45
- loggerChild.error(`Failed adding a revisions with operations`, error, {
46
- url,
47
- [MeltwaterAttributes.COMPANYID]: this.company._id,
48
- operations: JSON.stringify(operations)
49
- });
50
- return;
51
- }
52
- loggerChild.info(`Finished adding revision operations`, {
53
- operations: JSON.stringify(operations)
54
- });
55
- return result;
29
+ return await addRevisionsToDocuments(operations, this.company._id, logger);
56
30
  }
57
31
  async modifyDocuments(operations) {
58
32
  let retryAttempt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
59
33
  await this.init();
60
34
  const wait = 100;
61
- const url = `${this.documentServiceUrl}/`;
35
+ const url = `${documentServiceUrl}/`;
62
36
  const loggerChild = logger.child({
63
37
  [MeltwaterAttributes.COMPANYID]: this.company._id
64
38
  });
@@ -66,7 +40,7 @@ export class IRClient {
66
40
  try {
67
41
  result = await superagent.patch(url).query({
68
42
  wait,
69
- apikey: this.documentServiceKey
43
+ apikey: documentServiceKey
70
44
  }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
71
45
  } catch (err) {
72
46
  retryAttempt++;
@@ -87,7 +61,7 @@ export class IRClient {
87
61
  let retryAttempt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
88
62
  await this.init();
89
63
  try {
90
- const uri = `${this.documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${this.documentServiceKey}`;
64
+ const uri = `${documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${documentServiceKey}`;
91
65
  const metric = this.metrics.count(metricNames.irGetByDocumentId);
92
66
  const result = await superagent.get(uri);
93
67
  this.metrics.finishOperation(metricNames.irGetByDocumentId, metric);
@@ -128,8 +102,8 @@ export class IRClient {
128
102
  await this.init();
129
103
  const traceId = this.generateTraceId();
130
104
  try {
131
- let uri = `${this.exportApiUrl}?apikey=${this.exportApiKey}`;
132
- logger.info(`Starting call to : ${this.exportApiUrl}`, {
105
+ let uri = `${exportApiUrl}?apikey=${exportApiKey}`;
106
+ logger.info(`Starting call to : ${exportApiUrl}`, {
133
107
  rune
134
108
  });
135
109
  this.addCompanyToRuneForHorace(rune);
@@ -148,7 +122,7 @@ export class IRClient {
148
122
  await this.init();
149
123
  const traceId = this.generateTraceId();
150
124
  try {
151
- let uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
125
+ let uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
152
126
 
153
127
  // todo: remove changing rune searchResults size
154
128
  // todo: remove reduce ( dedupes and trims to original rune limit )
@@ -217,7 +191,7 @@ export class IRClient {
217
191
  async executeSearch(rune) {
218
192
  await this.init();
219
193
  this.addCompanyToRuneForHorace(rune);
220
- const uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
194
+ const uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
221
195
  return await superagent.post(uri).send(rune);
222
196
  }
223
197
  addCompanyToRuneForHorace(rune) {
@@ -231,4 +205,105 @@ export class IRClient {
231
205
  generateTraceId() {
232
206
  return `engage_${this.company._id}_${this.traceId}_${v4()}`;
233
207
  }
208
+ }
209
+ export async function addRevisionsToDocuments(operations, companyId, logger) {
210
+ const url = `${documentRevisionsServiceUrl}/${companyId}/`;
211
+ let result;
212
+ try {
213
+ result = await superagent.patch(url).query({
214
+ apikey: documentServiceKey
215
+ }).set('x-client-name', 'engage-conversations').send(operations).then(result => result.body);
216
+ } catch (error) {
217
+ loggerError(logger, `Failed adding a revisions with operations`, error, {
218
+ url,
219
+ [MeltwaterAttributes.COMPANYID]: companyId,
220
+ operations: JSON.stringify(operations)
221
+ });
222
+ return;
223
+ }
224
+ return result;
225
+ }
226
+ export async function markAsComplete(messageIds, companyId, logger) {
227
+ return reviseDocumentHelper(messageIds, 'markAsComplete', companyId, logger);
228
+ }
229
+ export async function markAsHasDMReply(messageIds, companyId, logger) {
230
+ return reviseDocumentHelper(messageIds, 'markAsHasDMReply', companyId, logger);
231
+ }
232
+ export async function markAsToDo(messageIds, companyId, logger) {
233
+ return reviseDocumentHelper(messageIds, 'markAsToDo', companyId, logger);
234
+ }
235
+ async function reviseDocumentHelper(messageIds, revisionType, companyId, logger) {
236
+ const messageIdsChunks = chunkArray(messageIds, chunkSize);
237
+ const revisionPromises = [];
238
+ for (const messageIdChunk of messageIdsChunks) {
239
+ const operations = {};
240
+ // build operation list for each document
241
+ switch (revisionType) {
242
+ case "markAsComplete":
243
+ messageIdChunk.forEach(message => {
244
+ operations[message.documentId] = [{
245
+ operation: 'addToSet',
246
+ fieldPath: 'metaData.applicationTags',
247
+ value: [generateCompanyAppDataKey({
248
+ companyId: companyId,
249
+ key: `${message.searchId || `credential:${message.credentialId}`}:status`,
250
+ value: 'complete'
251
+ })]
252
+ }];
253
+ });
254
+ break;
255
+ case "markAsHasDMReply":
256
+ messageIdChunk.forEach(message => {
257
+ operations[message.documentId] = [{
258
+ operation: 'addToSet',
259
+ fieldPath: 'metaData.applicationTags',
260
+ value: [generateCompanyAppDataKey({
261
+ companyId: companyId,
262
+ key: `${message.searchId || `credential:${message.credentialId}`}:hasBrandReply`,
263
+ value: 'true'
264
+ })]
265
+ }];
266
+ });
267
+ break;
268
+ case "markAsToDo":
269
+ messageIdChunk.forEach(message => {
270
+ operations[message.documentId] = [{
271
+ operation: 'removeFromSet',
272
+ fieldPath: 'metaData.applicationTags',
273
+ value: [generateCompanyAppDataKey({
274
+ companyId: companyId,
275
+ key: `${message.searchId || `credential:${message.credentialId}`}:status`,
276
+ value: `complete`
277
+ })]
278
+ }];
279
+ });
280
+ break;
281
+ }
282
+ try {
283
+ loggerInfo(logger, `Sending revision for ${revisionType} for companyId: ${companyId}`, {
284
+ operations: JSON.stringify(operations)
285
+ });
286
+ revisionPromises.push(addRevisionsToDocuments(operations, companyId, logger));
287
+ } catch (error) {
288
+ loggerError(logger, `Error marking as ${revisionType} in IR for ${messageIds.length} document(s). Unable to save revision.`, error, {
289
+ messageIds
290
+ });
291
+ }
292
+ }
293
+ return await Promise.all(revisionPromises);
294
+ }
295
+ function chunkArray(array, size) {
296
+ return array.reduce((chunks, item, index) => {
297
+ const chunkIndex = Math.floor(index / size);
298
+ chunks[chunkIndex] = (chunks[chunkIndex] || []).concat(item);
299
+ return chunks;
300
+ }, []);
301
+ }
302
+ function generateCompanyAppDataKey(_ref) {
303
+ let {
304
+ companyId,
305
+ key,
306
+ value
307
+ } = _ref;
308
+ return `conversations:${companyId}:${key}=${value}`;
234
309
  }
@@ -6,10 +6,6 @@ import crypto from 'crypto';
6
6
  import axios from 'axios';
7
7
  import fs from 'fs';
8
8
  import Twit from '@meltwater/social-twit';
9
- import { fileURLToPath } from 'url';
10
- import { dirname } from 'path';
11
- const __filename = fileURLToPath(import.meta.url);
12
- const __dirname = dirname(__filename);
13
9
  export function addOAuthToToken(token, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET) {
14
10
  if (!token.oauth) {
15
11
  token.oauth = OAuth({
@@ -511,8 +507,8 @@ async function downloadImage(url, fileName) {
511
507
  }
512
508
 
513
509
  // local
514
- function generateFilePath(mimeType) {
515
- let dir = `${__dirname}/temporaryTwitterMedia`;
510
+ function generateFilePath(mimeType, dirname) {
511
+ let dir = `${dirname}/temporaryTwitterMedia`;
516
512
  if (!fs.existsSync(dir)) {
517
513
  fs.mkdirSync(dir);
518
514
  }
@@ -526,7 +522,7 @@ function generateFilePath(mimeType) {
526
522
  // local
527
523
  async function uploadMedia(attachment, token, discussionType, logger) {
528
524
  return new Promise(async (resolve, reject) => {
529
- let filePath = generateFilePath(attachment.mimeType);
525
+ let filePath = generateFilePath(attachment.mimeType, attachment.__dirname);
530
526
  await downloadImage(attachment.url, filePath);
531
527
  const T = new Twit({
532
528
  consumer_key: token.consumer_key,
@@ -8,7 +8,7 @@ import { FeatureToggleClient } from './http/featureToggleApi.client.js';
8
8
  import { IdentityServicesClient } from './http/identityServices.client.js';
9
9
  import { InstagramApiClient } from './http/instagramApi.client.js';
10
10
  import { InstagramVideoClient } from './http/InstagramVideoClient.js';
11
- import { IRClient } from './http/ir.client.js';
11
+ import IRClient, * as IRClientHelpers from './http/ir.client.js';
12
12
  import { LinkedInApiClient, getOrganization, getProfile, getOrganizations } from './http/linkedInApi.client.js';
13
13
  import { TikTokApiClient } from './http/tiktokApi.client.js';
14
14
  import { MasfClient } from './http/masf.client.js';
@@ -57,5 +57,6 @@ export default {
57
57
  MasfClient,
58
58
  WarpZoneApiClient,
59
59
  DocumentHelperFunctions,
60
- LinkedInHelpers
60
+ LinkedInHelpers,
61
+ IRClientHelpers
61
62
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltwater/conversations-api-services",
3
- "version": "1.0.40",
3
+ "version": "1.0.41",
4
4
  "description": "Repository to contain all conversations api services shared across our services",
5
5
  "main": "dist/cjs/data-access/index.js",
6
6
  "module": "dist/esm/data-access/index.js",
@@ -2,10 +2,22 @@ import { v4 } from 'uuid';
2
2
  import superagent from 'superagent';
3
3
  import logger from '../../lib/logger.js';
4
4
  import configuration from '../../lib/configuration.js';
5
- import { metricN } from '../../lib/metrics.helper.js';
6
- import { MeltwaterAttributes } from '../../lib/logger.helpers.js';
5
+ import { loggerError, loggerInfo, MeltwaterAttributes } from '../../lib/logger.helpers.js';
7
6
 
8
- export class IRClient {
7
+ const searchServicesUrl = configuration.get('SEARCH_SERVICE_URL');
8
+ const searchServicesKey = process.env.IR_API_KEY;
9
+
10
+ const documentRevisionsServiceUrl = configuration.get(
11
+ 'DOCUMENT_REVISIONS_SERVICE_URL'
12
+ );
13
+ const documentServiceUrl = configuration.get('DOCUMENT_SERVICE_URL');
14
+ const documentServiceKey = process.env.IR_API_KEY;
15
+
16
+ const exportApiUrl = configuration.get('EXPORT_API_URL');
17
+ const exportApiKey = process.env.IR_API_KEY;
18
+ const chunkSize = process.env.REVISION_CHUNK_SIZE || 200;
19
+
20
+ export default class IRClient {
9
21
  RETRY_COUNT = 50;
10
22
  constructor(context) {
11
23
  const { company, user, traceId, metrics } = context;
@@ -15,62 +27,14 @@ export class IRClient {
15
27
  this.metrics = metrics;
16
28
  }
17
29
 
18
- async init() {
19
- let irApiKey = configuration.get('IR_API_KEY');
20
-
21
- this.searchServicesUrl = configuration.get('SEARCH_SERVICE_URL');
22
- this.searchServicesKey = irApiKey;
23
-
24
- this.documentRevisionsServiceUrl = configuration.get(
25
- 'DOCUMENT_REVISIONS_SERVICE_URL'
26
- );
27
- this.documentServiceUrl = configuration.get('DOCUMENT_SERVICE_URL');
28
- this.documentServiceKey = irApiKey;
29
-
30
- this.exportApiUrl = configuration.get('EXPORT_API_URL');
31
- this.exportApiKey = irApiKey;
32
- }
33
-
34
30
  async addRevisionsToDocuments(operations) {
35
- await this.init();
36
- const wait = 100;
37
- const url = `${this.documentRevisionsServiceUrl}/${this.company._id}/`;
38
- const loggerChild = logger.child({
39
- [MeltwaterAttributes.COMPANYID]: this.company._id,
40
- });
41
-
42
- let result;
43
-
44
- try {
45
- result = await superagent
46
- .patch(url)
47
- .query({
48
- wait,
49
- apikey: this.documentServiceKey,
50
- })
51
- .set('x-client-name', 'engage-conversations')
52
- .send(operations)
53
- .then((result) => result.body);
54
- } catch (error) {
55
- loggerChild.error(`Failed adding a revisions with operations`, error, {
56
- url,
57
- [MeltwaterAttributes.COMPANYID]: this.company._id,
58
- operations: JSON.stringify(operations)
59
- });
60
- return;
61
- }
62
-
63
- loggerChild.info(`Finished adding revision operations`, {
64
- operations: JSON.stringify(operations)
65
- });
66
-
67
- return result;
31
+ return await addRevisionsToDocuments(operations,this.company._id,logger);
68
32
  }
69
33
 
70
34
  async modifyDocuments(operations, retryAttempt = 0) {
71
35
  await this.init();
72
36
  const wait = 100;
73
- const url = `${this.documentServiceUrl}/`;
37
+ const url = `${documentServiceUrl}/`;
74
38
  const loggerChild = logger.child({
75
39
  [MeltwaterAttributes.COMPANYID]: this.company._id,
76
40
  });
@@ -81,7 +45,7 @@ export class IRClient {
81
45
  .patch(url)
82
46
  .query({
83
47
  wait,
84
- apikey: this.documentServiceKey,
48
+ apikey: documentServiceKey,
85
49
  })
86
50
  .set('x-client-name', 'engage-conversations')
87
51
  .send(operations)
@@ -111,7 +75,7 @@ export class IRClient {
111
75
  await this.init();
112
76
 
113
77
  try {
114
- const uri = `${this.documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${this.documentServiceKey}`;
78
+ const uri = `${documentServiceUrl}/${id}/revisions/${this.company._id}?apikey=${documentServiceKey}`;
115
79
  const metric = this.metrics.count(metricNames.irGetByDocumentId);
116
80
  const result = await superagent.get(uri);
117
81
  this.metrics.finishOperation(metricNames.irGetByDocumentId, metric);
@@ -164,9 +128,9 @@ export class IRClient {
164
128
  const traceId = this.generateTraceId();
165
129
 
166
130
  try {
167
- let uri = `${this.exportApiUrl}?apikey=${this.exportApiKey}`;
131
+ let uri = `${exportApiUrl}?apikey=${exportApiKey}`;
168
132
 
169
- logger.info(`Starting call to : ${this.exportApiUrl}`, {
133
+ logger.info(`Starting call to : ${exportApiUrl}`, {
170
134
  rune,
171
135
  });
172
136
 
@@ -197,7 +161,7 @@ export class IRClient {
197
161
  const traceId = this.generateTraceId();
198
162
 
199
163
  try {
200
- let uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
164
+ let uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
201
165
 
202
166
  // todo: remove changing rune searchResults size
203
167
  // todo: remove reduce ( dedupes and trims to original rune limit )
@@ -294,7 +258,7 @@ export class IRClient {
294
258
  async executeSearch(rune) {
295
259
  await this.init();
296
260
  this.addCompanyToRuneForHorace(rune);
297
- const uri = `${this.searchServicesUrl}?apikey=${this.searchServicesKey}`;
261
+ const uri = `${searchServicesUrl}?apikey=${searchServicesKey}`;
298
262
  return await superagent.post(uri).send(rune);
299
263
  }
300
264
 
@@ -311,3 +275,139 @@ export class IRClient {
311
275
  return `engage_${this.company._id}_${this.traceId}_${v4()}`;
312
276
  }
313
277
  }
278
+
279
+ export async function addRevisionsToDocuments(operations, companyId, logger) {
280
+ const url = `${documentRevisionsServiceUrl}/${companyId}/`;
281
+ let result;
282
+
283
+ try {
284
+ result = await superagent
285
+ .patch(url)
286
+ .query({
287
+ apikey: documentServiceKey,
288
+ })
289
+ .set('x-client-name', 'engage-conversations')
290
+ .send(operations)
291
+ .then((result) => result.body);
292
+ } catch (error) {
293
+ loggerError(logger,`Failed adding a revisions with operations`, error, {
294
+ url,
295
+ [MeltwaterAttributes.COMPANYID]: companyId,
296
+ operations: JSON.stringify(operations)
297
+ });
298
+ return;
299
+ }
300
+
301
+ return result;
302
+ }
303
+
304
+ export async function markAsComplete(messageIds, companyId, logger) {
305
+ return reviseDocumentHelper(messageIds,'markAsComplete',companyId,logger);
306
+ }
307
+
308
+ export async function markAsHasDMReply(messageIds, companyId, logger) {
309
+ return reviseDocumentHelper(messageIds,'markAsHasDMReply',companyId,logger);
310
+ }
311
+
312
+ export async function markAsToDo(messageIds, companyId, logger) {
313
+ return reviseDocumentHelper(messageIds,'markAsToDo',companyId,logger);
314
+ }
315
+
316
+
317
+ async function reviseDocumentHelper(messageIds, revisionType, companyId, logger) {
318
+ const messageIdsChunks = chunkArray(messageIds, chunkSize);
319
+ const revisionPromises = [];
320
+ for (const messageIdChunk of messageIdsChunks) {
321
+ const operations = {};
322
+ // build operation list for each document
323
+ switch(revisionType){
324
+ case "markAsComplete":
325
+ messageIdChunk.forEach((message) => {
326
+ operations[message.documentId] = [
327
+ {
328
+ operation: 'addToSet',
329
+ fieldPath: 'metaData.applicationTags',
330
+ value: [
331
+ generateCompanyAppDataKey({
332
+ companyId: companyId,
333
+ key: `${
334
+ message.searchId || `credential:${message.credentialId}`
335
+ }:status`,
336
+ value: 'complete',
337
+ }),
338
+ ],
339
+ },
340
+ ];
341
+ })
342
+ break;
343
+ case "markAsHasDMReply":
344
+ messageIdChunk.forEach((message) => {
345
+ operations[message.documentId] = [
346
+ {
347
+ operation: 'addToSet',
348
+ fieldPath: 'metaData.applicationTags',
349
+ value: [
350
+ generateCompanyAppDataKey({
351
+ companyId: companyId,
352
+ key: `${
353
+ message.searchId || `credential:${message.credentialId}`
354
+ }:hasBrandReply`,
355
+ value: 'true',
356
+ }),
357
+ ],
358
+ },
359
+ ];
360
+ })
361
+ break;
362
+ case "markAsToDo":
363
+ messageIdChunk.forEach((message) => {
364
+ operations[message.documentId] = [
365
+ {
366
+ operation: 'removeFromSet',
367
+ fieldPath: 'metaData.applicationTags',
368
+ value: [
369
+ generateCompanyAppDataKey({
370
+ companyId: companyId,
371
+ key: `${
372
+ message.searchId || `credential:${message.credentialId}`
373
+ }:status`,
374
+ value: `complete`,
375
+ }),
376
+ ],
377
+ },
378
+ ];
379
+ });
380
+ break;
381
+ }
382
+
383
+ try {
384
+ loggerInfo(logger,
385
+ `Sending revision for ${revisionType} for companyId: ${companyId}`,
386
+ { operations: JSON.stringify(operations) }
387
+ );
388
+ revisionPromises.push(
389
+ addRevisionsToDocuments(operations,companyId,logger)
390
+ );
391
+ } catch (error) {
392
+ loggerError(logger,
393
+ `Error marking as ${revisionType} in IR for ${messageIds.length} document(s). Unable to save revision.`,
394
+ error,
395
+ { messageIds }
396
+ );
397
+ }
398
+ }
399
+ return await Promise.all(revisionPromises);
400
+ }
401
+
402
+
403
+ function chunkArray(array, size) {
404
+ return array.reduce((chunks, item, index) => {
405
+ const chunkIndex = Math.floor(index / size);
406
+ chunks[chunkIndex] = (chunks[chunkIndex] || []).concat(item);
407
+ return chunks;
408
+ }, []);
409
+ }
410
+
411
+ function generateCompanyAppDataKey({ companyId, key, value }) {
412
+ return `conversations:${companyId}:${key}=${value}`;
413
+ }
@@ -7,11 +7,7 @@ import axios from 'axios';
7
7
  import fs from 'fs';
8
8
  import Twit from '@meltwater/social-twit';
9
9
 
10
- import { fileURLToPath } from 'url';
11
- import { dirname } from 'path';
12
10
 
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = dirname(__filename);
15
11
 
16
12
  export function addOAuthToToken(token, TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET) {
17
13
  if(!token.oauth){
@@ -610,8 +606,8 @@ async function downloadImage(url, fileName) {
610
606
  }
611
607
 
612
608
  // local
613
- function generateFilePath(mimeType) {
614
- let dir = `${__dirname}/temporaryTwitterMedia`;
609
+ function generateFilePath(mimeType, dirname) {
610
+ let dir = `${dirname}/temporaryTwitterMedia`;
615
611
  if (!fs.existsSync(dir)) {
616
612
  fs.mkdirSync(dir);
617
613
  }
@@ -625,7 +621,7 @@ function generateFilePath(mimeType) {
625
621
  // local
626
622
  async function uploadMedia(attachment, token, discussionType, logger) {
627
623
  return new Promise(async (resolve, reject) => {
628
- let filePath = generateFilePath(attachment.mimeType);
624
+ let filePath = generateFilePath(attachment.mimeType, attachment.__dirname);
629
625
  await downloadImage(attachment.url, filePath);
630
626
  const T = new Twit({
631
627
  consumer_key: token.consumer_key,
@@ -8,7 +8,7 @@ import { FeatureToggleClient } from './http/featureToggleApi.client.js';
8
8
  import { IdentityServicesClient } from './http/identityServices.client.js';
9
9
  import { InstagramApiClient } from './http/instagramApi.client.js';
10
10
  import { InstagramVideoClient } from './http/InstagramVideoClient.js';
11
- import { IRClient } from './http/ir.client.js';
11
+ import IRClient, * as IRClientHelpers from './http/ir.client.js';
12
12
  import {
13
13
  LinkedInApiClient,
14
14
  getOrganization,
@@ -65,4 +65,5 @@ export default {
65
65
  WarpZoneApiClient,
66
66
  DocumentHelperFunctions,
67
67
  LinkedInHelpers,
68
+ IRClientHelpers,
68
69
  };