@meltwater/conversations-api-services 1.0.0 → 1.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meltwater/conversations-api-services",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Repository to contain all conversations api services shared across our services",
5
5
  "type": "module",
6
6
  "main": "src/data-access/index.js",
@@ -107,7 +107,7 @@ export class FacebookApiClient {
107
107
  );
108
108
  logger.info(
109
109
  `Native Facebook API Publish Comment Response for documentId ${documentId}`,
110
- { status: response.status, ok: response.ok }
110
+ { responseBody: response.body }
111
111
  );
112
112
  return response.body;
113
113
  }
@@ -147,7 +147,7 @@ export class FacebookApiClient {
147
147
  );
148
148
  logger.info(
149
149
  `Native Facebook API Publish Private Message Response for documentId ${documentId}`,
150
- { status: response.status, ok: response.ok }
150
+ { responseBody: response.body }
151
151
  );
152
152
  return response.body;
153
153
  }
@@ -162,21 +162,23 @@ export class FacebookApiClient {
162
162
  .query({ access_token: accessToken, ...queryParams })
163
163
  .send(payload);
164
164
  } catch (err) {
165
- let errorText = '';
166
165
  if (
167
166
  err &&
168
167
  err.response &&
169
168
  err.response.body &&
170
169
  err.response.body.error
171
170
  ) {
172
- errorText = `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`;
173
- logger.error(errorText);
171
+ logger.error(
172
+ `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`
173
+ );
174
174
  } else {
175
- errorText = `Failed to call facebook api for documentId ${documentId}`;
176
- logger.error(errorText, err);
175
+ logger.error(
176
+ `Failed to call facebook api for documentId ${documentId}`,
177
+ err
178
+ );
177
179
  }
178
180
 
179
- throw new Error(errorText);
181
+ throw err;
180
182
  }
181
183
 
182
184
  if (response.status !== 200) {
@@ -204,21 +206,23 @@ export class FacebookApiClient {
204
206
  .query({ access_token: accessToken })
205
207
  .send(payload);
206
208
  } catch (err) {
207
- let errorText = '';
208
209
  if (
209
210
  err &&
210
211
  err.response &&
211
212
  err.response.body &&
212
213
  err.response.body.error
213
214
  ) {
214
- errorText = `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`;
215
- logger.error(errorText);
215
+ logger.error(
216
+ `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`
217
+ );
216
218
  } else {
217
- errorText = `Failed to call facebook api for documentId ${documentId}`;
218
- logger.error(errorText, err);
219
+ logger.error(
220
+ `Failed to call facebook api for documentId ${documentId}`,
221
+ err
222
+ );
219
223
  }
220
224
 
221
- throw new Error(errorText);
225
+ throw err;
222
226
  }
223
227
 
224
228
  if (response.status !== 200) {
@@ -246,21 +250,23 @@ export class FacebookApiClient {
246
250
  .query({ access_token: accessToken })
247
251
  .send(payload);
248
252
  } catch (err) {
249
- let errorText = '';
250
253
  if (
251
254
  err &&
252
255
  err.response &&
253
256
  err.response.body &&
254
257
  err.response.body.error
255
258
  ) {
256
- errorText = `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`;
257
- logger.error(errorText);
259
+ logger.error(
260
+ `Failed to call facebook api for documentId ${documentId}: ${err.response.body.error.message}`
261
+ );
258
262
  } else {
259
- errorText = `Failed to call facebook api for documentId ${documentId}`;
260
- logger.error(errorText, err);
263
+ logger.error(
264
+ `Failed to call facebook api for documentId ${documentId}`,
265
+ err
266
+ );
261
267
  }
262
268
 
263
- throw new Error(errorText);
269
+ throw err;
264
270
  }
265
271
 
266
272
  if (response.status !== 200) {
@@ -374,6 +380,13 @@ export class FacebookApiClient {
374
380
  );
375
381
  }
376
382
 
383
+ //sendNotification -> needs to wait for temp document to reach user
384
+ setTimeout(() => {
385
+ PushRepository.publish(
386
+ `${companyId}-${documentId}`,
387
+ updatedDocument
388
+ );
389
+ }, 5000);
377
390
  return apiResponse;
378
391
  }
379
392
 
@@ -381,7 +394,7 @@ export class FacebookApiClient {
381
394
  const {
382
395
  documentId,
383
396
  appData: { hidden: hiddenOnNative },
384
- metaData: { externalId },
397
+ metaData: { externalId: externalId },
385
398
  systemData: {
386
399
  connectionsCredential: credentialId,
387
400
  policies: { storage: { privateTo: companyId } = {} } = {},
@@ -445,6 +458,7 @@ export class FacebookApiClient {
445
458
  } = document;
446
459
 
447
460
  let banResponse;
461
+ let notificationPayload = document;
448
462
  let authorId = authors[0].authorInfo.externalId;
449
463
 
450
464
  try {
@@ -477,9 +491,30 @@ export class FacebookApiClient {
477
491
  `finished banning in facebook for ${authorId} on company ${companyId} `,
478
492
  { banResponse }
479
493
  );
494
+
495
+ notificationPayload.banStatus = {
496
+ code: banResponse.success === true ? 200 : 500,
497
+ message:
498
+ banResponse.success === true ? 'Success' : 'Failed to Ban',
499
+ };
500
+ logger.debug(
501
+ `Notification payload for ${authorId} on company ${companyId} `,
502
+ notificationPayload
503
+ );
480
504
  } catch (error) {
481
505
  logger.error(`${authorId} - error recieved - ${error.code}`, error);
506
+ // message failed ot send, mark as such
507
+ notificationPayload.banStatus = {
508
+ code: 405,
509
+ message: 'Exception Occurred',
510
+ };
482
511
  }
512
+ setTimeout(() => {
513
+ PushRepository.publish(
514
+ `${companyId}-${authorId}`,
515
+ notificationPayload
516
+ );
517
+ }, 5000);
483
518
  }
484
519
 
485
520
  async toggleLike(document) {
@@ -544,7 +579,7 @@ export class FacebookApiClient {
544
579
 
545
580
  logger.info(
546
581
  `Native Facebook API Like Response for documentId ${documentId}`,
547
- { status: response.status, ok: response.ok }
582
+ { responseBody: response.body }
548
583
  );
549
584
  return response.body;
550
585
  }
@@ -566,7 +601,7 @@ export class FacebookApiClient {
566
601
 
567
602
  logger.info(
568
603
  `Native Facebook API Unlike Response for documentId ${documentId}`,
569
- { status: response.status, ok: response.ok }
604
+ { responseBody: response.body }
570
605
  );
571
606
  return response.body;
572
607
  }
@@ -589,7 +624,7 @@ export class FacebookApiClient {
589
624
 
590
625
  logger.info(
591
626
  `Native Facebook API Hide Response for documentId ${documentId}`,
592
- { status: response.status, ok: response.ok }
627
+ { responseBody: response.body }
593
628
  );
594
629
  return response.body;
595
630
  }
@@ -612,7 +647,7 @@ export class FacebookApiClient {
612
647
 
613
648
  logger.info(
614
649
  `Native Facebook API Unhide Response for documentId ${documentId}`,
615
- { status: response.status, ok: response.ok }
650
+ { responseBody: response.body }
616
651
  );
617
652
  return response.body;
618
653
  }
@@ -648,7 +683,7 @@ export class FacebookApiClient {
648
683
  }
649
684
  logger.info(
650
685
  `Native Facebook API is User Banned Response was invalid for userId ${userId} ${credentialId}`,
651
- { status: response.status, ok: response.ok }
686
+ { responseBody: response }
652
687
  );
653
688
  return false;
654
689
  }
@@ -671,7 +706,7 @@ export class FacebookApiClient {
671
706
 
672
707
  logger.info(
673
708
  `Native Facebook API Hide Response for externalAuthorId ${authorId}`,
674
- { status: response.status, ok: response.ok }
709
+ { responseBody: response.body }
675
710
  );
676
711
  return response.body;
677
712
  }
@@ -694,7 +729,7 @@ export class FacebookApiClient {
694
729
 
695
730
  logger.info(
696
731
  `Native Facebook API Unban Response for externalAuthorId ${authorId}`,
697
- { status: response.status, ok: response.ok }
732
+ { responseBody: response.body }
698
733
  );
699
734
  return response.body;
700
735
  }
@@ -3,6 +3,9 @@ import logger from '../../lib/logger.js';
3
3
  import { removePrefix } from '../../lib/externalId.helpers.js';
4
4
  import assert from 'assert';
5
5
  import { CredentialsApiClient } from '../http/credentialsApi.client.js';
6
+ // import { messagesRepository } from '@data-access/mongo/repository/messages.js';
7
+ // import PushRepository from '@data-access/redis/repository/push.repository.js';
8
+ // import { DOCUMENT_STATUS } from '@data-access/mongo/query.helpers.js';
6
9
 
7
10
  const INSTAGRAM_URL = 'https://graph.facebook.com';
8
11
  export class InstagramApiClient {
@@ -44,22 +47,22 @@ export class InstagramApiClient {
44
47
  .query(queryStringArgs)
45
48
  .send(data);
46
49
  } catch (err) {
47
- let errorText = '';
48
50
  if (
49
51
  err &&
50
52
  err.response &&
51
53
  err.response.body &&
52
54
  err.response.body.error
53
55
  ) {
54
- errorText =
55
- 'Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}';
56
- logger.error(errorText);
56
+ logger.error(
57
+ `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`
58
+ );
57
59
  } else {
58
- errorText =
59
- 'Failed to call instagram api for documentId ${documentId}';
60
- logger.error(errorText, err);
60
+ logger.error(
61
+ `Failed to call instagram api for documentId ${documentId}`,
62
+ err
63
+ );
61
64
  }
62
- throw new Error(errorText);
65
+ throw err;
63
66
  }
64
67
 
65
68
  if (response.status !== 200) {
@@ -97,7 +100,7 @@ export class InstagramApiClient {
97
100
 
98
101
  logger.info(
99
102
  `Native Instagram API getPost Response for documentId ${documentId}`,
100
- { status: response.status, ok: response.ok }
103
+ { responseBody: response.body }
101
104
  );
102
105
  return response.body;
103
106
  } catch (err) {
@@ -124,20 +127,22 @@ export class InstagramApiClient {
124
127
  .query({ message: text })
125
128
  .send();
126
129
  } catch (err) {
127
- let errorText = '';
128
130
  if (
129
131
  err &&
130
132
  err.response &&
131
133
  err.response.body &&
132
134
  err.response.body.error
133
135
  ) {
134
- errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
135
- logger.error(errorText);
136
+ logger.error(
137
+ `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`
138
+ );
136
139
  } else {
137
- errorText = `Failed to call instagram api for documentId ${documentId}`;
138
- logger.error(errorText, err);
140
+ logger.error(
141
+ `Failed to call instagram api for documentId ${documentId}`,
142
+ err
143
+ );
139
144
  }
140
- throw new Error(errorText);
145
+ throw err;
141
146
  }
142
147
 
143
148
  if (response.status !== 200) {
@@ -168,15 +173,18 @@ export class InstagramApiClient {
168
173
  .query({ hide: hideStatus })
169
174
  .send();
170
175
  } catch (err) {
171
- let errorText = '';
172
176
  if (err && err.response && err.response.body) {
173
- errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
174
- logger.error(errorText, err.response.body.error);
177
+ logger.error(
178
+ `Failed to call instagram api for documentId ${documentId}`,
179
+ err.response.body.error
180
+ );
175
181
  } else {
176
- errorText = `Failed to call instagram api for documentId ${documentId}`;
177
- logger.error(errorText, err);
182
+ logger.error(
183
+ `Failed to call instagram api for documentId ${documentId}`,
184
+ err
185
+ );
178
186
  }
179
- throw new Error(errorText);
187
+ throw err;
180
188
  }
181
189
 
182
190
  if (response.status !== 200) {
@@ -207,15 +215,18 @@ export class InstagramApiClient {
207
215
  .query({ fields: fields })
208
216
  .send();
209
217
  } catch (err) {
210
- let errorText = '';
211
218
  if (err && err.response && err.response.body) {
212
- errorText = `Failed to call instagram api for documentId ${documentId}: ${err.response.body.error.message}`;
213
- logger.error(errorText, err.response.body.error);
219
+ logger.error(
220
+ `Failed to call instagram api for documentId ${documentId}`,
221
+ err.response.body.error
222
+ );
214
223
  } else {
215
- errorText = `Failed to call instagram api for documentId ${documentId}`;
216
- logger.error(errorText, err);
224
+ logger.error(
225
+ `Failed to call instagram api for documentId ${documentId}`,
226
+ err
227
+ );
217
228
  }
218
- throw new Error(errorText);
229
+ throw err;
219
230
  }
220
231
 
221
232
  if (response.status !== 200) {
@@ -267,7 +278,7 @@ export class InstagramApiClient {
267
278
 
268
279
  logger.info(
269
280
  `Native Intagram API Publish Comment Response for documentId ${documentId}`,
270
- { status: response.status, ok: response.ok }
281
+ { responseBody: response.body }
271
282
  );
272
283
  return response.body;
273
284
  }
@@ -316,7 +327,7 @@ export class InstagramApiClient {
316
327
 
317
328
  logger.info(
318
329
  `Native Instagram API Publish Reply Response for documentId ${documentId}`,
319
- { status: response.status, ok: response.ok }
330
+ { responseBody: response.body }
320
331
  );
321
332
  return response.body;
322
333
  }
@@ -402,10 +413,51 @@ export class InstagramApiClient {
402
413
  default:
403
414
  throw new Error('Unsupported discussion type');
404
415
  }
416
+
417
+ if (apiResponse) {
418
+ let findQuery = { documentId };
419
+ findQuery['systemData.policies.storage.privateTo'] = companyId;
420
+ let updateObj = {};
421
+ updateObj['systemData.status'] = DOCUMENT_STATUS.SUCCESS;
422
+ // update with the new id from instagram reply/comment or direct message
423
+ updateObj['metaData.externalId'] =
424
+ apiResponse.id || `${apiResponse.message_id}`;
425
+ updateObj.documentId = Buffer.from(
426
+ `${updateObj['metaData.externalId']}`
427
+ ).toString('base64');
428
+ updatedDocument = await messagesRepository.updateOne(
429
+ findQuery,
430
+ updateObj
431
+ );
432
+ } else {
433
+ throw new Error(
434
+ 'Invalid response from instagram publish endpoints'
435
+ );
436
+ }
405
437
  } catch (err) {
406
438
  logger.error(documentId + ' - exception details ' + err, err);
439
+ let findQuery = { documentId };
440
+ findQuery['systemData.policies.storage.privateTo'] = companyId;
441
+
442
+ let updateQuery = {};
443
+ updateQuery['systemData.status'] = DOCUMENT_STATUS.FAILED;
444
+ updateQuery['systemData.error'] =
445
+ err && err.response && err.response.error
446
+ ? err.response.error
447
+ : err;
448
+ updatedDocument = await messagesRepository.updateOne(
449
+ findQuery,
450
+ updateQuery
451
+ );
407
452
  }
408
453
 
454
+ //sendNotification -> needs to wait for temp document to reach user
455
+ setTimeout(() => {
456
+ PushRepository.publish(
457
+ `${companyId}-${documentId}`,
458
+ updatedDocument
459
+ );
460
+ }, 5000);
409
461
  return apiResponse;
410
462
  }
411
463
 
@@ -427,7 +479,7 @@ export class InstagramApiClient {
427
479
 
428
480
  logger.info(
429
481
  `Native Instagram API Hide Response for documentId ${documentId}`,
430
- { status: response.status, ok: response.ok }
482
+ { responseBody: response.body }
431
483
  );
432
484
  return response.body;
433
485
  }
@@ -450,7 +502,7 @@ export class InstagramApiClient {
450
502
 
451
503
  logger.info(
452
504
  `Native Instagram API Unhide Response for documentId ${documentId}`,
453
- { status: response.status, ok: response.ok }
505
+ { responseBody: response.body }
454
506
  );
455
507
  return response.body;
456
508
  }
@@ -489,7 +541,7 @@ export class InstagramApiClient {
489
541
 
490
542
  logger.info(
491
543
  `Native Instagram API Publish Private Message Response for documentId ${documentId}`,
492
- { status: response.status, ok: response.ok }
544
+ { responseBody: response.body }
493
545
  );
494
546
  return response.body;
495
547
  }
@@ -1,11 +1,13 @@
1
- import superagent from 'superagent';
2
- import configuration from '../../lib/configuration.js';
3
- import { CredentialsApiClient } from '../http/credentialsApi.client.js';
4
- import logger from '../../lib/logger.js';
1
+ import superagentfrom from 'superagent';
2
+ import configurationfrom from '../../lib/configuration.js';
3
+ // import { DOCUMENT_STATUS } from '@data-access/mongo/query.helpers.js';
4
+ // import { messagesRepository } from '@data-access/mongo/repository/messages.js';
5
+ // import PushRepositoryfrom from '@data-access/redis/repository/push.repository.js';
5
6
 
6
7
  export class LinkedInApiClient {
7
- constructor() {
8
- this.credentialsAPI = new CredentialsApiClient();
8
+ constructor({ services }) {
9
+ this.tokenService = services.token;
10
+ this.logger = services.logger;
9
11
  }
10
12
 
11
13
  async likeMessage(document) {
@@ -41,12 +43,12 @@ export class LinkedInApiClient {
41
43
  )}?actor=${fixedEncodeURIComponent(payload.actor)}`;
42
44
 
43
45
  try {
44
- let credential = await this.credentialsAPI.getToken(
46
+ let credential = await this.tokenService.getByCredentialId(
45
47
  credentialId,
46
48
  companyId
47
49
  );
48
50
  if (!likedByUser) {
49
- logger.debug(`${documentId} - trying Delete `, {
51
+ this.logger.debug(`${documentId} - trying Delete `, {
50
52
  query,
51
53
  payload,
52
54
  });
@@ -61,7 +63,7 @@ export class LinkedInApiClient {
61
63
  'LinkedIn-Version': '202305',
62
64
  });
63
65
  } else {
64
- logger.debug(`${documentId} - sending to linkedin `, {
66
+ this.logger.debug(`${documentId} - sending to linkedin `, {
65
67
  query,
66
68
  payload,
67
69
  });
@@ -78,17 +80,12 @@ export class LinkedInApiClient {
78
80
  .send(payload);
79
81
  }
80
82
 
81
- logger.info(
83
+ this.logger.info(
82
84
  `Native Linkedin API Like Comment Response for documentId ${documentId}`,
83
- {
84
- ok: response.ok,
85
- status: response.status,
86
- message: JSON.parse(response.text),
87
- likedByUser,
88
- }
85
+ { response, likedByUser }
89
86
  );
90
87
  } catch (err) {
91
- logger.error(documentId + ' - exception details', {
88
+ this.logger.error(documentId + ' - exception details', {
92
89
  err,
93
90
  });
94
91
  throw err;
@@ -107,28 +104,22 @@ export class LinkedInApiClient {
107
104
  },
108
105
  metaData: {
109
106
  externalId,
110
- source: { id: actorURN },
111
- inReplyTo: { id: inReplyToId },
107
+ inReplyTo: { id: inReplyToId},
112
108
  },
113
109
  } = document;
114
110
 
115
- const [, comment] = externalId.split(',');
116
- const commentId = comment.split(')')[0];
117
-
118
111
  let response;
119
112
  let query = `${configuration.get(
120
- 'LINKEDIN_API'
121
- )}/socialActions/${fixedEncodeURIComponent(
122
- inReplyToId
123
- )}/comments/${commentId}?actor=${fixedEncodeURIComponent(actorURN)}`;
113
+ 'LINKEDIN_API'
114
+ )}/socialActions/${fixedEncodeURIComponent(inReplyToId)}/comments/${fixedEncodeURIComponent(externalId)}`;
124
115
 
125
116
  try {
126
- let credential = await this.credentialsAPI.getToken(
117
+ let credential = await this.tokenService.getByCredentialId(
127
118
  credentialId,
128
119
  companyId
129
120
  );
130
-
131
- logger.debug(`${documentId} - trying Delete `, {
121
+
122
+ this.logger.debug(`${documentId} - trying Delete `, {
132
123
  query,
133
124
  });
134
125
  response = await superagent
@@ -139,25 +130,18 @@ export class LinkedInApiClient {
139
130
  'X-RestLi-Protocol-Version': configuration.get(
140
131
  'LINKEDIN_API_VERSION'
141
132
  ),
142
- 'LinkedIn-Version': '202305',
133
+ 'LinkedIn-Version': '202401',
143
134
  });
144
135
 
145
- logger.info(
136
+ this.logger.info(
146
137
  `Native Linkedin API Delete Comment Response for documentId ${documentId}`,
147
- { status: response.status, ok: response.ok }
138
+ { response }
148
139
  );
149
140
  } catch (err) {
150
- let errorText = '';
151
- if (err && err.response && err.response.text) {
152
- errorText = `Call to linkedin api with documentId ${documentId} failed`;
153
- logger.error(errorText, {
154
- message: JSON.parse(err.response.text),
155
- });
156
- } else {
157
- errorText = `Call to linkedin api with documentId ${documentId} failed`;
158
- logger.error(errorText, err);
159
- }
160
- throw new Error(errorText);
141
+ this.logger.error(documentId + ' - exception details', {
142
+ err,
143
+ });
144
+ throw err;
161
145
  }
162
146
  return response;
163
147
  }
@@ -219,7 +203,7 @@ export class LinkedInApiClient {
219
203
  // data-listener -> linkedin_api.client sendComment
220
204
  let response, updatedDocument;
221
205
  try {
222
- let credential = await this.credentialsAPI.getToken(
206
+ let credential = await this.tokenService.getByCredentialId(
223
207
  credentialId,
224
208
  companyId
225
209
  );
@@ -241,13 +225,52 @@ export class LinkedInApiClient {
241
225
  status: 200,
242
226
  message: JSON.parse(response.text),
243
227
  };
228
+
229
+ //markMessageAsSuccess
230
+ let findQuery = { documentId };
231
+ findQuery['systemData.policies.storage.privateTo'] = companyId;
232
+ let updateQuery = {};
233
+ const { $URN: externalId, actor } = response.message;
234
+
235
+ updateQuery['systemData.status'] = DOCUMENT_STATUS.SUCCESS;
236
+ updateQuery['authors[0].authorInfo.externalId'] = actor;
237
+ updateQuery['metaData.externalId'] = externalId;
238
+ updateQuery.documentId = Buffer.from(`${externalId}`).toString(
239
+ 'base64'
240
+ );
241
+
242
+ updatedDocument = await messagesRepository.updateOne(
243
+ findQuery,
244
+ updateQuery
245
+ );
244
246
  } catch (err) {
245
- logger.error(
247
+ this.logger.error(
246
248
  documentId + ' - exception linkedin publish ' + err,
247
249
  err
248
250
  );
251
+
252
+ let findQuery = { documentId };
253
+ findQuery['systemData.policies.storage.privateTo'] = companyId;
254
+ let updateQuery = {};
255
+ updateQuery['systemData.status'] = DOCUMENT_STATUS.FAILED;
256
+ updateQuery['systemData.error'] =
257
+ err && err.response && err.response.error
258
+ ? err.response.error
259
+ : err;
260
+ updatedDocument = await messagesRepository.updateOne(
261
+ findQuery,
262
+ updateQuery
263
+ );
249
264
  }
250
265
 
266
+ //sendNotification -> needs to wait for temp document to reach user
267
+ setTimeout(() => {
268
+ PushRepository.publish(
269
+ `${companyId}-${documentId}`,
270
+ updatedDocument
271
+ );
272
+ }, 5000);
273
+
251
274
  return response;
252
275
  }
253
276
 
@@ -268,7 +291,7 @@ export class LinkedInApiClient {
268
291
 
269
292
  let response;
270
293
  try {
271
- let credential = await this.credentialsAPI.getToken(
294
+ let credential = await this.tokenService.getByCredentialId(
272
295
  credentialId,
273
296
  companyId
274
297
  );
@@ -292,7 +315,7 @@ export class LinkedInApiClient {
292
315
  };
293
316
  return response;
294
317
  } catch (err) {
295
- logger.error(
318
+ this.logger.error(
296
319
  documentId + ' - exception linkedin media register ' + err,
297
320
  err
298
321
  );
@@ -320,14 +343,16 @@ export class LinkedInApiClient {
320
343
  })
321
344
  .then((result) => result.body);
322
345
  } catch (error) {
323
- logger.error(
346
+ this.logger.error(
324
347
  `Failed requesting LinkedIn API for profileId ${urn}`,
325
348
  error
326
349
  );
327
350
  return;
328
351
  }
329
352
 
330
- logger.info(`Finished requesting LinkedIn API for profileId ${urn}`);
353
+ this.logger.info(
354
+ `Finished requesting LinkedIn API for profileId ${urn}`
355
+ );
331
356
 
332
357
  return profile;
333
358
  }
@@ -353,13 +378,13 @@ export class LinkedInApiClient {
353
378
  })
354
379
  .then((result) => result.body);
355
380
  } catch (error) {
356
- logger.error(
381
+ this.logger.error(
357
382
  `Failed requesting LinkedIn API for organizationId ${urn}`,
358
383
  error
359
384
  );
360
385
  }
361
386
 
362
- logger.info(
387
+ this.logger.info(
363
388
  `Finished requesting LinkedIn API for organizationId ${urn}`
364
389
  );
365
390
 
@@ -385,14 +410,14 @@ export class LinkedInApiClient {
385
410
  'LinkedIn-Version': '202305',
386
411
  });
387
412
  } catch (error) {
388
- logger.error('Error in getAllStats ' + error, error);
413
+ this.logger.error('Error in getAllStats ' + error, error);
389
414
  }
390
415
 
391
416
  let { commentSummary, reactionSummaries } = response.body || {};
392
417
  if (commentSummary && reactionSummaries) {
393
418
  return { commentSummary, reactionSummaries };
394
419
  } else {
395
- logger.error(
420
+ this.logger.error(
396
421
  `Invalid response getting all Linkedin Stats for externalId = ${externalId} ` +
397
422
  response,
398
423
  response
@@ -413,14 +438,14 @@ export class LinkedInApiClient {
413
438
  reactionSummaries,
414
439
  };
415
440
  } catch (error) {
416
- logger.error('Error in getSocialStats: ' + error, error);
441
+ this.logger.error('Error in getSocialStats: ' + error, error);
417
442
  }
418
443
  }
419
444
 
420
445
  async getImageMedia(mediaId, credentialId, companyId) {
421
446
  let response = {};
422
447
 
423
- let credential = await this.credentialsAPI.getToken(
448
+ let credential = await this.tokenService.getByCredentialId(
424
449
  credentialId,
425
450
  companyId
426
451
  );
@@ -448,14 +473,14 @@ export class LinkedInApiClient {
448
473
  'LinkedIn-Version': '202305',
449
474
  });
450
475
  } catch (error) {
451
- logger.error('Error in getImageMedia ' + error, error);
476
+ this.logger.error('Error in getImageMedia ' + error, error);
452
477
  }
453
478
 
454
479
  let { downloadUrl } = response.body || {};
455
480
  if (downloadUrl) {
456
481
  return { downloadUrl };
457
482
  } else {
458
- logger.error(
483
+ this.logger.error(
459
484
  `Invalid response getting Linkedin media for mediaId = ${mediaId} ` +
460
485
  response,
461
486
  response
@@ -468,7 +493,7 @@ export class LinkedInApiClient {
468
493
  let response = {};
469
494
  let tempValue;
470
495
 
471
- let credential = await this.credentialsAPI.getToken(
496
+ let credential = await this.tokenService.getByCredentialId(
472
497
  credentialId,
473
498
  companyId
474
499
  );
@@ -504,14 +529,14 @@ export class LinkedInApiClient {
504
529
  'LinkedIn-Version': '202305',
505
530
  });
506
531
  } catch (error) {
507
- logger.error('Error in getVideoMedia ' + error, error);
532
+ this.logger.error('Error in getVideoMedia ' + error, error);
508
533
  }
509
534
 
510
535
  let { thumbnail, downloadUrl } = response.body || {};
511
536
  if (downloadUrl) {
512
537
  return { thumbnail, downloadUrl };
513
538
  } else {
514
- logger.error(
539
+ this.logger.error(
515
540
  `Invalid response getting Linkedin media for mediaId = ${mediaId} ` +
516
541
  response,
517
542
  response
@@ -552,7 +577,7 @@ export class LinkedInApiClient {
552
577
 
553
578
  return {};
554
579
  } catch (error) {
555
- logger.error('Error in getMedia: ' + error, error);
580
+ this.logger.error('Error in getMedia: ' + error, error);
556
581
  }
557
582
  }
558
583
 
@@ -564,7 +589,7 @@ export class LinkedInApiClient {
564
589
  ) {
565
590
  let result;
566
591
  try {
567
- let credential = await this.credentialsAPI.getToken(
592
+ let credential = await this.tokenService.getByCredentialId(
568
593
  credentialId,
569
594
  companyId
570
595
  );
@@ -586,7 +611,7 @@ export class LinkedInApiClient {
586
611
  })
587
612
  .then((result) => result.body);
588
613
  } catch (error) {
589
- logger.error('Error in getLikesByUser: ' + error, error);
614
+ this.logger.error('Error in getLikesByUser: ' + error, error);
590
615
  return false;
591
616
  }
592
617
  return true;
@@ -595,7 +620,7 @@ export class LinkedInApiClient {
595
620
  async uploadMedia(query) {
596
621
  const { socialAccountId, url, token, data } = query;
597
622
 
598
- logger.info(
623
+ this.logger.info(
599
624
  `Starting to upload media to linkedin for ${socialAccountId}`,
600
625
  { url: query.url }
601
626
  );
@@ -613,7 +638,7 @@ export class LinkedInApiClient {
613
638
  })
614
639
  .send(data)
615
640
  .catch((err) => {
616
- logger.error(err);
641
+ this.logger.error(err);
617
642
  throw err;
618
643
  });
619
644
  }
@@ -10,7 +10,6 @@ import { InstagramApiClient } from './http/instagramApi.client.js';
10
10
  import { InstagramVideoClient } from './http/InstagramVideoClient.js';
11
11
  import { IRClient } from './http/ir.client.js';
12
12
  import { LinkedInApiClient } from './http/linkedInApi.client.js';
13
- import { TikTokApiClient } from './http/tiktokApi.client.js';
14
13
  import { MasfClient } from './http/masf.client.js';
15
14
  import { WarpZoneApiClient } from './http/WarpZoneApi.client.js';
16
15
 
@@ -27,7 +26,6 @@ export {
27
26
  InstagramVideoClient,
28
27
  IRClient,
29
28
  LinkedInApiClient,
30
- TikTokApiClient,
31
29
  MasfClient,
32
30
  WarpZoneApiClient,
33
31
  };
@@ -1,42 +0,0 @@
1
- name: Release
2
- on:
3
- push:
4
- branches:
5
- - main
6
-
7
- permissions:
8
- contents: read # for checkout
9
-
10
- jobs:
11
- release:
12
- name: Release
13
- runs-on: ubuntu-latest
14
- permissions:
15
- contents: write # to be able to publish a GitHub release
16
- issues: write # to be able to comment on released issues
17
- pull-requests: write # to be able to comment on released pull requests
18
- id-token: write # to enable use of OIDC for npm provenance
19
- steps:
20
- - name: Checkout
21
- uses: actions/checkout@v3
22
- with:
23
- fetch-depth: 0
24
- persist-credential: false
25
- token: ${{ secrets.GH_TOKEN }}
26
- - name: Setup Node.js
27
- uses: actions/setup-node@v3
28
- with:
29
- node-version: "lts/*"
30
- - name: NPM Auth Setup
31
- run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
32
- - name: NPM whoami
33
- run: npm whoami
34
- - name: Install dependencies
35
- run: npm clean-install
36
- - name: Verify signatures
37
- run: npm audit signatures
38
- - name: Release
39
- env:
40
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
41
- GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
42
- run: npx semantic-release
@@ -1,477 +0,0 @@
1
- import superagent from 'superagent';
2
- import configuration from '../../lib/configuration.js';
3
- import logger from '../../lib/logger.js';
4
- import { removePrefix } from '../../lib/externalId.helpers.js';
5
- import { CredentialsApiClient } from '../http/credentialsApi.client.js';
6
-
7
- export class TikTokApiClient {
8
- constructor() {
9
- this.tiktokURL = configuration.get('TIKTOK_API_URL');
10
- this.credentialsAPI = new CredentialsApiClient();
11
- }
12
-
13
- async getAuthorization(document) {
14
- const {
15
- documentId,
16
- systemData: {
17
- connectionsCredential: credentialId,
18
- policies: { storage: { privateTo: companyId } = {} } = {},
19
- } = {},
20
- } = document;
21
-
22
- try {
23
- const credential = await this.credentialsAPI.getToken(
24
- credentialId,
25
- companyId
26
- );
27
- return `${credential.token}`;
28
- } catch (error) {
29
- logger.error(
30
- `${documentId} - error getting tiktok token - ${error.code}`,
31
- error
32
- );
33
- }
34
- }
35
-
36
- async sendPost({
37
- paramString = '',
38
- headers = {},
39
- document,
40
- postData = undefined,
41
- }) {
42
- let response = {};
43
- try {
44
- response = await superagent
45
- .post(this.tiktokURL + paramString)
46
- .set('Accept', 'application/json')
47
- .set('Content-Type', 'application/json')
48
- .set('Access-Token', await this.getAuthorization(document))
49
- .send(postData);
50
- } catch (err) {
51
- let errorText = '';
52
- if (
53
- err &&
54
- err.response &&
55
- err.response.body &&
56
- err.response.body.error
57
- ) {
58
- errorText = `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`;
59
- logger.error(errorText);
60
- } else {
61
- errorText = `Failed to call tiktok api for paramString ${paramString}`;
62
- logger.error(errorText, err);
63
- }
64
-
65
- throw new Error(errorText);
66
- }
67
-
68
- return response;
69
- }
70
-
71
- // assumes batch calls are using the same credential
72
- async sendRequest({ paramString = '', headers = {}, document }) {
73
- let response = {};
74
- try {
75
- response = await superagent
76
- .get(this.tiktokURL + paramString)
77
- .set('Accept', 'application/json')
78
- .set('Content-Type', 'application/json')
79
- .set('Access-Token', await this.getAuthorization(document))
80
- .send();
81
- } catch (err) {
82
- let errorText = '';
83
- if (
84
- err &&
85
- err.response &&
86
- err.response.body &&
87
- err.response.body.error
88
- ) {
89
- errorText = `Failed to call tiktok api for paramString ${paramString}: ${err.response.body.error.message}`;
90
- logger.error(errorText);
91
- } else {
92
- errorText = `Failed to call tiktok api for paramString ${paramString}`;
93
- logger.error(errorText, err);
94
- }
95
-
96
- throw new Error(errorText);
97
- }
98
-
99
- if (response.status !== 200) {
100
- logger.error(
101
- `Failed to call tiktok api for documentId ${documentId}`,
102
- response.body
103
- );
104
- let error = new Error(
105
- `Failed to call tiktok api for documentId ${documentId}`
106
- );
107
- error.code = response.status;
108
- throw error;
109
- }
110
-
111
- return response.body;
112
- }
113
-
114
- async getChannelInfoForDocuments(documents) {
115
- const channelInfo = await this.getStatsForDocuments(
116
- documents,
117
- 'metaData.authors.0.authorInfo.externalId',
118
- {
119
- og: ['video/list/'],
120
- }
121
- );
122
- return channelInfo;
123
- }
124
-
125
- async getStatsForDocuments(
126
- documents,
127
- externalIdFrom = 'metaData.externalId',
128
- paramsByType = {
129
- og: ['video/list/'],
130
- }
131
- ) {
132
- // for batching separate og and comment/reply requests per credential
133
- const documentsByTypeAndCredentials = documents.reduce(
134
- (retObj, document, index) => {
135
- const paramArray =
136
- paramsByType[document.metaData.discussionType];
137
- if (paramArray) {
138
- paramArray.forEach((paramString) => {
139
- let discussionTypeBucket =
140
- retObj[
141
- `${paramString}${document.systemData.connectionsCredential}`
142
- ];
143
- if (!discussionTypeBucket) {
144
- discussionTypeBucket = {
145
- paramString,
146
- document,
147
- externalIds: [],
148
- externalIdToDocumentIndex: {},
149
- };
150
- retObj[
151
- `${paramString}${document.systemData.connectionsCredential}`
152
- ] = discussionTypeBucket;
153
- }
154
- const externalIdTrimmed = removePrefix(
155
- // javascript weirdness to get around '.' notation
156
- externalIdFrom
157
- .split('.')
158
- .reduce((a, b) => a[b], document)
159
- );
160
- discussionTypeBucket.externalIds.push(
161
- externalIdTrimmed
162
- );
163
- discussionTypeBucket.externalIdToDocumentIndex[
164
- externalIdTrimmed
165
- ] = index;
166
- });
167
- }
168
- return retObj;
169
- },
170
- {}
171
- );
172
-
173
- const sortedResults = Array(documents.length).fill();
174
- await Promise.all(
175
- Object.values(documentsByTypeAndCredentials).map(
176
- async (documentsByTypeAndCredential) => {
177
- if (documentsByTypeAndCredential.externalIds.length) {
178
- const {
179
- metaData: {
180
- inReplyTo: { profileId: business_id },
181
- },
182
- } = documentsByTypeAndCredential.document;
183
- const trimmedBusinessId = removePrefix(business_id);
184
- const requestResult = await this.sendRequest({
185
- paramString: `${
186
- documentsByTypeAndCredential.paramString
187
- }?business_id=${trimmedBusinessId}&fields=["item_id","create_time","thumbnail_url","share_url","embed_url","caption","video_views","likes","comments","shares"]&filters=${JSON.stringify(
188
- {
189
- video_ids:
190
- documentsByTypeAndCredential.externalIds,
191
- }
192
- )}`,
193
- document: documentsByTypeAndCredential.document,
194
- });
195
- requestResult.data.videos.forEach(
196
- ({
197
- item_id,
198
- create_time,
199
- thumbnail_url,
200
- share_url,
201
- embed_url,
202
- caption,
203
- video_views,
204
- likes,
205
- comments,
206
- shares,
207
- ...args
208
- }) => {
209
- const documentIndex =
210
- documentsByTypeAndCredential
211
- .externalIdToDocumentIndex[item_id];
212
- sortedResults[documentIndex] = {
213
- ...sortedResults[documentIndex],
214
- item_id,
215
- create_time,
216
- thumbnail_url,
217
- externalId:
218
- documents[documentIndex].metaData
219
- .externalId,
220
- share_url,
221
- embed_url,
222
- caption,
223
- video_views,
224
- likes,
225
- comments,
226
- shares,
227
- };
228
- }
229
- );
230
- }
231
- }
232
- )
233
- );
234
- return sortedResults;
235
- }
236
-
237
- async publish(message, videoId, markMessageAsCompleteDocumentId) {
238
- const {
239
- metaData: { discussionType },
240
- } = message;
241
- let publishedMessage;
242
-
243
- switch (discussionType) {
244
- case 'qt':
245
- publishedMessage = await this.insertComment(message);
246
- break;
247
- case 're':
248
- publishedMessage = await this.insertReply(
249
- videoId,
250
- message,
251
- markMessageAsCompleteDocumentId
252
- );
253
- break;
254
- }
255
-
256
- return publishedMessage;
257
- }
258
-
259
- async insertComment(document) {
260
- const {
261
- body: {
262
- content: { text: text },
263
- },
264
- metaData: {
265
- source: { id: business_id },
266
- inReplyTo: { id: video_id },
267
- },
268
- } = document;
269
- const { body: publishedMessage } =
270
- (await this.sendPost({
271
- paramString: 'comment/create/',
272
- postData: {
273
- business_id,
274
- video_id,
275
- text,
276
- },
277
- document,
278
- })) || {};
279
- return publishedMessage;
280
- }
281
-
282
- async insertReply(videoId, document, markMessageAsCompleteDocumentId) {
283
- const {
284
- body: {
285
- content: { text: text },
286
- },
287
- metaData: {
288
- inReplyTo: { id: comment_id, profileId: business_id },
289
- },
290
- } = document;
291
-
292
- const { body: publishedMessage } =
293
- (await this.sendPost({
294
- paramString: 'comment/reply/create/',
295
- postData: {
296
- business_id: document.metaData.source.id,
297
- comment_id: markMessageAsCompleteDocumentId || comment_id,
298
- video_id: removePrefix(videoId),
299
- text,
300
- },
301
- document,
302
- })) || {};
303
-
304
- return publishedMessage;
305
- }
306
-
307
- async toggleLike(document) {
308
- const {
309
- enrichments: {
310
- socialScores: { tt_liked_by_user: likedByUser },
311
- },
312
- } = document;
313
-
314
- try {
315
- let likeResponse = await (!likedByUser
316
- ? this.like(document)
317
- : this.unlike(document));
318
- return likeResponse;
319
- } catch (error) {
320
- logger.error(`${document} - error recieved - ${error.code}`, error);
321
- throw error;
322
- }
323
- }
324
-
325
- async toggleHide(document) {
326
- const {
327
- documentId,
328
- appData: { hidden: hiddenOnNative },
329
- } = document;
330
-
331
- let response;
332
- try {
333
- if (hiddenOnNative) {
334
- response = await this.hide(document);
335
- } else {
336
- response = await this.unhide(document);
337
- }
338
- logger.info(
339
- `Native Tiktok API Hide Response for documentId ${documentId}`,
340
- { status: response.status, ok: response.ok }
341
- );
342
-
343
- if (response.message === 'OK') {
344
- return response;
345
- }
346
- } catch (error) {
347
- logger.error(
348
- `${documentId} - error recieved - ${error.code}`,
349
- error
350
- );
351
- }
352
- }
353
-
354
- async like(document) {
355
- const {
356
- metaData: {
357
- externalId: comment_id,
358
- source: { id: sourceId },
359
- },
360
- } = document;
361
- const { body: publishedMessage } =
362
- (await this.sendPost({
363
- paramString: 'comment/like/',
364
- postData: {
365
- business_id: removePrefix(sourceId),
366
- comment_id: removePrefix(comment_id),
367
- action: 'LIKE',
368
- },
369
- document,
370
- })) || {};
371
- return publishedMessage;
372
- }
373
-
374
- async unlike(document) {
375
- const {
376
- metaData: {
377
- inReplyTo: { id: comment_id },
378
- source: { id: sourceId },
379
- },
380
- } = document;
381
- const { body: publishedMessage } =
382
- (await this.sendPost({
383
- paramString: 'comment/like/',
384
- postData: {
385
- business_id: removePrefix(sourceId),
386
- comment_id: removePrefix(comment_id),
387
- action: 'unlike',
388
- },
389
- document,
390
- })) || {};
391
- return publishedMessage;
392
- }
393
-
394
- async hide(document) {
395
- const {
396
- metaData: {
397
- externalId: comment_id,
398
- source: { id: sourceId },
399
- inReplyTo: { id: video_id },
400
- },
401
- } = document;
402
- const { body: publishedMessage } =
403
- (await this.sendPost({
404
- paramString: 'comment/hide/',
405
- postData: {
406
- business_id: removePrefix(sourceId),
407
- comment_id: removePrefix(comment_id),
408
- video_id: removePrefix(video_id),
409
- action: 'HIDE',
410
- },
411
- document,
412
- })) || {};
413
- return publishedMessage;
414
- }
415
-
416
- async unhide(document) {
417
- const {
418
- metaData: {
419
- externalId: comment_id,
420
- source: { id: sourceId },
421
- inReplyTo: { id: video_id },
422
- },
423
- } = document;
424
- const { body: publishedMessage } =
425
- (await this.sendPost({
426
- paramString: 'comment/hide/',
427
- postData: {
428
- business_id: removePrefix(sourceId),
429
- comment_id: removePrefix(comment_id),
430
- video_id: removePrefix(video_id),
431
- action: 'UNHIDE',
432
- },
433
- document,
434
- })) || {};
435
- return publishedMessage;
436
- }
437
-
438
- async deleteComment(document) {
439
- const {
440
- metaData: {
441
- inReplyTo: { id: comment_id, profileId: business_id },
442
- },
443
- } = document;
444
- const { body: publishedMessage } =
445
- (await this.sendPost({
446
- paramString: 'comment/delete/',
447
- postData: {
448
- business_id,
449
- comment_id,
450
- },
451
- document,
452
- })) || {};
453
- return publishedMessage;
454
- }
455
-
456
- async getProfile(business_id, document) {
457
- const fields = '["username", "display_name", "profile_image"]';
458
- const profile =
459
- (await this.sendRequest({
460
- paramString: `get/?business_id=${business_id}&fields=${fields}`,
461
- document,
462
- })) || {};
463
- return profile;
464
- }
465
-
466
- async getPostData(business_id, video_id, document) {
467
- const fields =
468
- '["item_id", "thumbnail_url", "create_time", "username", "display_name", "profile_image"]';
469
- const video_ids = `["${video_id}"]`;
470
- const profile =
471
- (await this.sendRequest({
472
- paramString: `video/list?business_id=${business_id}&fields=${fields}&filters={"video_ids":${video_ids}}`,
473
- document,
474
- })) || {};
475
- return profile;
476
- }
477
- }