@meltwater/conversations-api-services 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.drone.yml +13 -0
- package/.github/workflows/release.yml +42 -0
- package/.nvmrc +1 -0
- package/README.md +91 -0
- package/catalog-info.yaml +12 -0
- package/docs/index.md +28 -0
- package/mkdocs.yml +8 -0
- package/package.json +45 -0
- package/src/data-access/http/InstagramVideoClient.js +37 -0
- package/src/data-access/http/WarpZoneApi.client.js +39 -0
- package/src/data-access/http/amazonS3.js +41 -0
- package/src/data-access/http/asset-manager-tvm.client.js +32 -0
- package/src/data-access/http/companiesApi.client.js +35 -0
- package/src/data-access/http/credentialsApi.client.js +137 -0
- package/src/data-access/http/entitlementsApi.client.js +41 -0
- package/src/data-access/http/facebookApi.client.js +701 -0
- package/src/data-access/http/featureToggleApi.client.js +35 -0
- package/src/data-access/http/identityServices.client.js +117 -0
- package/src/data-access/http/instagramApi.client.js +496 -0
- package/src/data-access/http/ir.client.js +309 -0
- package/src/data-access/http/linkedInApi.client.js +624 -0
- package/src/data-access/http/masf.client.js +93 -0
- package/src/data-access/http/tiktokApi.client.js +477 -0
- package/src/data-access/index.js +33 -0
- package/src/lib/applicationTags.helpers.js +22 -0
- package/src/lib/configuration.js +10 -0
- package/src/lib/document-action-events.js +6 -0
- package/src/lib/externalId.helpers.js +15 -0
- package/src/lib/hiddenComment.helper.js +100 -0
- package/src/lib/logger.js +14 -0
- package/src/lib/metrics.helper.js +107 -0
package/.drone.yml
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
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
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
20.11.0
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# conversations-api-services
|
|
2
|
+
|
|
3
|
+
Repository to contain all conversations api services shared across our services
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This section should answer:
|
|
8
|
+
|
|
9
|
+
> What is this?
|
|
10
|
+
|
|
11
|
+
Adding visuals to this project description will make your project more memorable and the README easier to understand.
|
|
12
|
+
|
|
13
|
+
Great examples:
|
|
14
|
+
|
|
15
|
+
- [meltwater/contact-source-service](https://github.com/meltwater/contact-source-service) - _It is the backend behind the Influencers application used by Meltwater customers. It provides sources/contacts management and search capabilities._
|
|
16
|
+
- [release-it/release-it](https://github.com/release-it/release-it) - _Generic CLI tool to automate versioning and package publishing related tasks_
|
|
17
|
+
|
|
18
|
+
## Product / Business Purpose
|
|
19
|
+
|
|
20
|
+
An explanation of the business purpose of this repo, e.g. the Meltwater Product that this ties into. This section should answer:
|
|
21
|
+
|
|
22
|
+
> Why does this exist?
|
|
23
|
+
|
|
24
|
+
Great examples:
|
|
25
|
+
|
|
26
|
+
- [meltwater/chat-message-integration](https://github.com/meltwater/chat-message-integration) _The chat-message-integration project is the API that is used to configure and handle messages for Slack and Teams integrations in the MI App._
|
|
27
|
+
- [meltwater/explore-compare-service](https://github.com/meltwater/explore-compare-service) - _Compares in Explorer allow users to run several saved queries at the same time and do a comparison._
|
|
28
|
+
|
|
29
|
+
## Maintainers
|
|
30
|
+
|
|
31
|
+
Meltwater's current ownership model assumes that every piece of software running in Production is owned by a team. This section should answer:
|
|
32
|
+
|
|
33
|
+
> Who owns this?
|
|
34
|
+
> Who to contact for help?
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
> Maintained by TEAM_NAME (TEAM_EMAIL_ADDRESS@meltwater.com).
|
|
39
|
+
> Contact us via #TEAM_SLACK_CHANNEL in Slack.
|
|
40
|
+
|
|
41
|
+
## Contributing
|
|
42
|
+
|
|
43
|
+
Make it easier for people to understand how they can contribute to your project. Remember that these people could be from your own team, or another team.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
> We welcome and appreciate any and all contributions, even if it is a typo fix to this README.
|
|
48
|
+
> When working on a larger contribution, please open an issue first, so that we can discuss the approach.
|
|
49
|
+
> Once you have your contribution ready, please open a Pull Requests containing your changes and contact us via #TEAM_SLACK_CHANNEL in Slack.
|
|
50
|
+
|
|
51
|
+
## How do I use it? (Installation)
|
|
52
|
+
|
|
53
|
+
Instructions targeted at the **user** of your project, rather than at a co-developer in your team. This section should answer:
|
|
54
|
+
|
|
55
|
+
> How to use this?
|
|
56
|
+
|
|
57
|
+
If this is an internal tool that is just used within your team, then you can share this here as well.
|
|
58
|
+
|
|
59
|
+
## Development Guide
|
|
60
|
+
|
|
61
|
+
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. This section should answer:
|
|
62
|
+
|
|
63
|
+
> How to get started with developing on this project?
|
|
64
|
+
|
|
65
|
+
This is essential for onboarding new members on your team but also for allowing people from outside of your team to contribute.
|
|
66
|
+
|
|
67
|
+
This Development Guide may contain different sub-sections, depending on what stack/ecosystem your project is using. Below some examples of sub-sections that apply to most projects.
|
|
68
|
+
|
|
69
|
+
### Prerequisites
|
|
70
|
+
|
|
71
|
+
What things do you need to install the software and how to install them? Including links here is helpful.
|
|
72
|
+
|
|
73
|
+
### Running it locally
|
|
74
|
+
|
|
75
|
+
A step-by-step series of examples that tell you how to get a development env running.
|
|
76
|
+
|
|
77
|
+
End with an example of getting some data out of the system or using it for a little demo.
|
|
78
|
+
|
|
79
|
+
### Running the tests
|
|
80
|
+
|
|
81
|
+
Explain how to run the automated tests for this system.
|
|
82
|
+
|
|
83
|
+
### Deployment
|
|
84
|
+
|
|
85
|
+
Add additional notes about how to deploy this on a live system.
|
|
86
|
+
You may also want to link to the different environments where this is deployed (e.g. Staging, Production).
|
|
87
|
+
|
|
88
|
+
### Monitoring & Troubleshooting
|
|
89
|
+
|
|
90
|
+
How can you inspect the system running in remote environments (Staging, Production)?
|
|
91
|
+
This should include logs, dashboards, etc.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
apiVersion: backstage.io/v1alpha1
|
|
2
|
+
kind: Component
|
|
3
|
+
metadata:
|
|
4
|
+
name: "conversations-api-services"
|
|
5
|
+
description: "Repository to contain all conversations api services shared across our services"
|
|
6
|
+
annotations:
|
|
7
|
+
github.com/project-slug: meltwater/conversations-api-services
|
|
8
|
+
backstage.io/techdocs-ref: dir:.
|
|
9
|
+
spec:
|
|
10
|
+
type: "module"
|
|
11
|
+
lifecycle: experimental
|
|
12
|
+
owner: "group:default/phoenix"
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
## conversations-api-services
|
|
2
|
+
|
|
3
|
+
Repository to contain all conversations api services shared across our services
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
Start write your documentation by adding more markdown (.md) files to this folder (/docs) or replace the content in this file.
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
The Table of Contents on the right is generated automatically based on the hierarchy
|
|
12
|
+
of headings. Only use one H1 (`#` in Markdown) per file.
|
|
13
|
+
|
|
14
|
+
## Site navigation
|
|
15
|
+
|
|
16
|
+
For new pages to appear in the left hand navigation you need edit the `mkdocs.yml`
|
|
17
|
+
file in root of your repo. The navigation can also link out to other sites.
|
|
18
|
+
|
|
19
|
+
Alternatively, if there is no `nav` section in `mkdocs.yml`, a navigation section
|
|
20
|
+
will be created for you. However, you will not be able to use alternate titles for
|
|
21
|
+
pages, or include links to other sites.
|
|
22
|
+
|
|
23
|
+
Note that MkDocs uses `mkdocs.yml`, not `mkdocs.yaml`, although both appear to work.
|
|
24
|
+
See also <https://www.mkdocs.org/user-guide/configuration/>.
|
|
25
|
+
|
|
26
|
+
## Support
|
|
27
|
+
|
|
28
|
+
That's it. If you need support, reach out in [#docs-like-code](https://discord.com/channels/687207715902193673/714754240933003266) on Discord.
|
package/mkdocs.yml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@meltwater/conversations-api-services",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Repository to contain all conversations api services shared across our services",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/data-access/index.js",
|
|
7
|
+
"directories": {
|
|
8
|
+
"doc": "docs"
|
|
9
|
+
},
|
|
10
|
+
"prettier": "@meltwater/engage-prettier-config",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/meltwater/conversations-api-services.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/meltwater/conversations-api-services/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/meltwater/conversations-api-services#readme",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@hapi/joi": "^17.1.1",
|
|
27
|
+
"@meltwater/date-range": "^3.6.0",
|
|
28
|
+
"@meltwater/engage-conversations-schemas": "^2.3.10",
|
|
29
|
+
"@meltwater/xrunes": "^1.1.1",
|
|
30
|
+
"@meltwater/xrunes-core": "^3.0.0",
|
|
31
|
+
"apollo-server-hapi": "^3.13.0",
|
|
32
|
+
"aws-sdk": "^2.1562.0",
|
|
33
|
+
"dataloader": "^2.2.2",
|
|
34
|
+
"html-entities": "^2.4.0",
|
|
35
|
+
"jsonwebtoken": "^9.0.2",
|
|
36
|
+
"lodash": "^4.17.21",
|
|
37
|
+
"moment": "^2.30.1",
|
|
38
|
+
"prom-client": "^15.1.0",
|
|
39
|
+
"superagent": "^8.1.2",
|
|
40
|
+
"winston": "^3.11.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@meltwater/engage-prettier-config": "^1.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import configuration from '../../lib/configuration.js';
|
|
3
|
+
import logger from '../../lib/logger.js';
|
|
4
|
+
|
|
5
|
+
export class InstagramVideoClient {
|
|
6
|
+
/**
|
|
7
|
+
* @private
|
|
8
|
+
* @param {string} url
|
|
9
|
+
* @returns {Promise<object>}
|
|
10
|
+
*/
|
|
11
|
+
_getDownloadLink = async (url) => {
|
|
12
|
+
const apiUrl = configuration.get('UGC_MEDIA_API_URL');
|
|
13
|
+
const apiToken = configuration.get('UGC_MEDIA_API_TOKEN');
|
|
14
|
+
try {
|
|
15
|
+
const response = await superagent
|
|
16
|
+
.get(`${apiUrl}/instagram/video/`)
|
|
17
|
+
.set('x-api-token', apiToken)
|
|
18
|
+
.query({ url });
|
|
19
|
+
|
|
20
|
+
return response.body?.data;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error(`Could not retrieve video link from ${url}`, {
|
|
23
|
+
error,
|
|
24
|
+
});
|
|
25
|
+
throw new Error('Could not retrieve video link');
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param {string} url
|
|
32
|
+
* @returns {Promise<object>}
|
|
33
|
+
*/
|
|
34
|
+
getVideoLink = async (url) => {
|
|
35
|
+
return await this._getDownloadLink(url);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class WarpZoneApiClient {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.warpzoneURL = configuration.get('WARPZONE_API_URL');
|
|
8
|
+
this.warpzoneAPI = configuration.get('WARPZONE_API_KEY');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async sendPost(postData = undefined) {
|
|
12
|
+
let response = {};
|
|
13
|
+
try {
|
|
14
|
+
response = await superagent
|
|
15
|
+
.post(this.warpzoneURL + 'event')
|
|
16
|
+
.set('Accept', 'application/json')
|
|
17
|
+
.set('Content-Type', 'application/json')
|
|
18
|
+
.set('X-API-Key', this.warpzoneAPI)
|
|
19
|
+
.send(postData);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
if (
|
|
22
|
+
err &&
|
|
23
|
+
err.response &&
|
|
24
|
+
err.response.body &&
|
|
25
|
+
err.response.body.error
|
|
26
|
+
) {
|
|
27
|
+
logger.error(
|
|
28
|
+
`Failed to call warp zone api: ${err.response.body.error.message}`
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
logger.error(`Failed to call warp zone api`, err);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return response;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import AWS from 'aws-sdk';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
class AWS_S3 {
|
|
5
|
+
constructor() {}
|
|
6
|
+
|
|
7
|
+
async getClient() {
|
|
8
|
+
const {
|
|
9
|
+
Credentials: { AccessKeyId, SecretAccessKey, SessionToken } = {},
|
|
10
|
+
} = await assetManagerTvmRepository.get(companyId);
|
|
11
|
+
|
|
12
|
+
if (!AccessKeyId || !SecretAccessKey || !SessionToken) {
|
|
13
|
+
// probably a bit extreme, what would you prefer to do
|
|
14
|
+
throw 'No Credentials available';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return new AWS.S3({
|
|
18
|
+
accessKeyId: AccessKeyId,
|
|
19
|
+
secretAccessKey: SecretAccessKey,
|
|
20
|
+
sessionToken: SessionToken,
|
|
21
|
+
useAccelerateEndpoint: true,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
GetS3ObjectQuery(bucket, filename) {
|
|
26
|
+
assert(bucket, 'S3 bucket is required');
|
|
27
|
+
assert(filename, 'filePath is required');
|
|
28
|
+
assert(typeof filename === 'string', 'filePath should be a string.');
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
config: {
|
|
32
|
+
Bucket: bucket,
|
|
33
|
+
Key: filename.replace(`/${bucket}/`, ''),
|
|
34
|
+
GrantRead:
|
|
35
|
+
'uri=http://acs.amazonaws.com/groups/global/AllUsers',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const awsS3Client = new AWS_S3();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
|
|
4
|
+
const { ASSET_MANAGER_TVM_API_KEY, ASSET_MANAGER_TVM_URL } = process.env;
|
|
5
|
+
|
|
6
|
+
class AssetManagerTVMRepository {
|
|
7
|
+
apiKey;
|
|
8
|
+
constructor() {}
|
|
9
|
+
|
|
10
|
+
async get(companyId) {
|
|
11
|
+
let response;
|
|
12
|
+
try {
|
|
13
|
+
response = await superagent
|
|
14
|
+
.get(`https://${ASSET_MANAGER_TVM_URL}/tvm`)
|
|
15
|
+
.query({
|
|
16
|
+
companyId,
|
|
17
|
+
})
|
|
18
|
+
.timeout(5000)
|
|
19
|
+
.set({
|
|
20
|
+
'x-api-key': ASSET_MANAGER_TVM_API_KEY,
|
|
21
|
+
})
|
|
22
|
+
.then((result) => result.body);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error(error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return response;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const assetManagerTvmRepository = new AssetManagerTVMRepository();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class CompanyApiClient {
|
|
6
|
+
constructor({ services }) {
|
|
7
|
+
this.companiesApiUrl = configuration.get('COMPANIES_API_URL');
|
|
8
|
+
|
|
9
|
+
this.authService = services.auth;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async getById(companyId, jwt, retries = 0) {
|
|
13
|
+
let company;
|
|
14
|
+
try {
|
|
15
|
+
let resignedToken = await this.authService.getResignedToken(jwt);
|
|
16
|
+
company = await superagent
|
|
17
|
+
.get(`${this.companiesApiUrl}/${companyId}`)
|
|
18
|
+
.set('Authorization', resignedToken)
|
|
19
|
+
.set('x-client-name', configuration.get('CLIENT_NAME_HEADER'))
|
|
20
|
+
.then((result) => result.body);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error(
|
|
23
|
+
`Failed requesting Companies Api for companyId ${companyId} retry ${retries}`,
|
|
24
|
+
error
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (retries <= 3) {
|
|
28
|
+
return this.getById(companyId, jwt, ++retries);
|
|
29
|
+
}
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return company;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import superagent from 'superagent'; // @todo remove superagent
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class CredentialsApiClient {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.credentialsApiUrl = configuration.get('CREDENTIALS_API_URL');
|
|
8
|
+
this.credentialsApiApplicationId = configuration.get(
|
|
9
|
+
'CREDENTIALS_API_APPLICATION_ID'
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getCredential(
|
|
14
|
+
{ companyId, userId, accountId, productArea },
|
|
15
|
+
userPermissionsEnabled
|
|
16
|
+
) {
|
|
17
|
+
const credentials = await this.getCredentialsByCompany(
|
|
18
|
+
{ companyId, userId, productArea },
|
|
19
|
+
userPermissionsEnabled
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
if (!credentials) {
|
|
23
|
+
throw 'No credentials returned';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const credential = credentials.find(
|
|
27
|
+
(credential) => credential.social_account_id == accountId
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
if (!credential) {
|
|
31
|
+
throw `${sourceId} credential was not found.`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return credential;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async getCredentialsByCompany(
|
|
38
|
+
{ companyId, userId, productArea },
|
|
39
|
+
userPermissionsEnabled
|
|
40
|
+
) {
|
|
41
|
+
let credentials;
|
|
42
|
+
try {
|
|
43
|
+
if (productArea) {
|
|
44
|
+
logger.info(
|
|
45
|
+
`Get Credentials for company: ${companyId} and user: ${userId}`
|
|
46
|
+
);
|
|
47
|
+
credentials = await superagent
|
|
48
|
+
.get(
|
|
49
|
+
`${this.credentialsApiUrl}/api/v4.0/CompanyCredentials/${this.credentialsApiApplicationId}/${companyId}/${userId}`
|
|
50
|
+
)
|
|
51
|
+
.query({
|
|
52
|
+
productArea,
|
|
53
|
+
})
|
|
54
|
+
.then((result) => result.body.data);
|
|
55
|
+
} else if (userPermissionsEnabled) {
|
|
56
|
+
credentials = await superagent
|
|
57
|
+
.get(
|
|
58
|
+
`${this.credentialsApiUrl}/api/v4.0/CompanyCredentials/${this.credentialsApiApplicationId}/${companyId}/${userId}`
|
|
59
|
+
)
|
|
60
|
+
.then((result) => result.body.data);
|
|
61
|
+
} else {
|
|
62
|
+
credentials = await superagent
|
|
63
|
+
.get(
|
|
64
|
+
`${this.credentialsApiUrl}/api/v3.0/CompanyCredentials/${this.credentialsApiApplicationId}/${companyId}/${userId}`
|
|
65
|
+
)
|
|
66
|
+
.then((result) => result.body.data);
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error(
|
|
70
|
+
`Failed requesting Credentials API for companyId ${companyId} ${userId}`,
|
|
71
|
+
error
|
|
72
|
+
);
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
logger.info(
|
|
77
|
+
`Finished requesting Credentials API for companyId ${companyId}`
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return credentials;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// todo: will this need to ever use productArea?
|
|
84
|
+
async getToken(credentialId, companyId) {
|
|
85
|
+
let token;
|
|
86
|
+
try {
|
|
87
|
+
token = await superagent
|
|
88
|
+
.get(
|
|
89
|
+
`${this.credentialsApiUrl}/api/Token/${credentialId}/${companyId}`
|
|
90
|
+
)
|
|
91
|
+
.then((result) => {
|
|
92
|
+
return result.body;
|
|
93
|
+
});
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger.error(
|
|
96
|
+
`Failed requesting Credentials API for credentialId ${credentialId} and companyId ${companyId}`,
|
|
97
|
+
error
|
|
98
|
+
);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
logger.info(
|
|
103
|
+
`Finished requesting Credentials API for credentialId ${credentialId} and companyId ${companyId}`
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return token;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async getCredentialIdByAccountId(accountId, channel) {
|
|
110
|
+
let credentialId;
|
|
111
|
+
try {
|
|
112
|
+
credentialId = await superagent
|
|
113
|
+
.get(
|
|
114
|
+
`${this.credentialsApiUrl}/api/CompanyCredentials/GetCredential/${channel}/${accountId}`
|
|
115
|
+
)
|
|
116
|
+
.then((result) => {
|
|
117
|
+
return (
|
|
118
|
+
result.body &&
|
|
119
|
+
result.body.data &&
|
|
120
|
+
result.body.data.credentialId
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logger.error(
|
|
125
|
+
`Failed requesting Credentials API for credentialId ${accountId} and channel ${channel}`,
|
|
126
|
+
error
|
|
127
|
+
);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
logger.info(
|
|
132
|
+
`Finished requesting Credentials API for credentialId ${accountId} and channel ${channel}`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return credentialId;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import logger from '../../lib/logger.js';
|
|
3
|
+
import configuration from '../../lib/configuration.js';
|
|
4
|
+
|
|
5
|
+
export class EntitlementsApiClient {
|
|
6
|
+
constructor({ services }) {
|
|
7
|
+
this.entitlementsApiUrl = configuration.get('ENTITLEMENTS_API_URL');
|
|
8
|
+
|
|
9
|
+
this.authService = services.auth;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async getById(entitlementId, companyId, jwt, retries = 0) {
|
|
13
|
+
let entitlement;
|
|
14
|
+
try {
|
|
15
|
+
let resignedToken = await this.authService.getResignedToken(jwt);
|
|
16
|
+
entitlement = await superagent
|
|
17
|
+
.get(
|
|
18
|
+
`${this.entitlementsApiUrl}/company/${companyId}/${entitlementId}`
|
|
19
|
+
)
|
|
20
|
+
.set('Authorization', resignedToken)
|
|
21
|
+
.set('x-client-name', configuration.get('CLIENT_NAME_HEADER'))
|
|
22
|
+
.then((result) => result.body);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error(
|
|
25
|
+
`Failed requesting entitlement ${entitlementId} for companyId ${companyId} retry ${retries}`,
|
|
26
|
+
error
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (retries <= 3) {
|
|
30
|
+
return this.getById(entitlementId, companyId, jwt, ++retries);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
logger.info(
|
|
36
|
+
`Finished requesting entitlement ${entitlementId} for companyId ${companyId}`
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return entitlement;
|
|
40
|
+
}
|
|
41
|
+
}
|