@unito/integration-cli 0.60.2 → 0.60.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/boilerplate/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
20.9
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# Build
|
|
5
5
|
#
|
|
6
6
|
|
|
7
|
-
FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/node:20-alpine as build
|
|
7
|
+
FROM --platform=$BUILDPLATFORM public.ecr.aws/docker/library/node:20.9-alpine as build
|
|
8
8
|
|
|
9
9
|
WORKDIR /build
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ RUN npm run compile
|
|
|
19
19
|
# Runtime
|
|
20
20
|
#
|
|
21
21
|
|
|
22
|
-
FROM --platform=$TARGETPLATFORM public.ecr.aws/docker/library/node:20-alpine as runtime
|
|
22
|
+
FROM --platform=$TARGETPLATFORM public.ecr.aws/docker/library/node:20.9-alpine as runtime
|
|
23
23
|
|
|
24
24
|
WORKDIR /app
|
|
25
25
|
|
|
@@ -20,13 +20,13 @@
|
|
|
20
20
|
"email": "hello@unito.io"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
|
-
"node": "
|
|
23
|
+
"node": "20.9"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@unito/integration-sdk": "^0.x"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@types/node": "20.x",
|
|
29
|
+
"@types/node": "20.9.x",
|
|
30
30
|
"typescript-eslint": "7.x",
|
|
31
31
|
"eslint": "8.x",
|
|
32
32
|
"npm-audit-resolver": "^3.0.0-RC.0",
|
|
@@ -41,9 +41,6 @@ class OAuth2Service {
|
|
|
41
41
|
*/
|
|
42
42
|
constructor(authorizationInfo, environment = globalConfiguration_1.Environment.Production, credentialPayload) {
|
|
43
43
|
const { clientId, clientSecret, authorizationUrl, scopes, tokenUrl, grantType, requestContentType, refreshRequestParameters, tokenRequestParameters, } = authorizationInfo;
|
|
44
|
-
this.startServer = this.startServer.bind(this);
|
|
45
|
-
this.stopServer = this.stopServer.bind(this);
|
|
46
|
-
this.handleCallback = this.handleCallback.bind(this);
|
|
47
44
|
this.clientId = clientId;
|
|
48
45
|
this.clientSecret = clientSecret;
|
|
49
46
|
this.providerAuthorizationUrl = authorizationUrl;
|
|
@@ -99,7 +96,8 @@ class OAuth2Service {
|
|
|
99
96
|
// Error response: https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2.1
|
|
100
97
|
const error = req.query.error;
|
|
101
98
|
if (error) {
|
|
102
|
-
res.setHeader('Content-Type', 'text/html')
|
|
99
|
+
res.setHeader('Content-Type', 'text/html');
|
|
100
|
+
res.send(exports.HTML_ERROR_MSG);
|
|
103
101
|
return;
|
|
104
102
|
}
|
|
105
103
|
// We keep all the non-standard query parameters of the authorization response
|
|
@@ -130,16 +128,17 @@ class OAuth2Service {
|
|
|
130
128
|
])),
|
|
131
129
|
};
|
|
132
130
|
try {
|
|
133
|
-
const
|
|
131
|
+
const tokenResponse = await fetch((0, template_1.expandTemplate)(this.tokenUrl, templateVariables, { urlEncodeVariables: true }), {
|
|
134
132
|
headers: tokenRequestHeaders,
|
|
135
133
|
body: this.encodeBody(tokenRequestPayload, this.requestContentType),
|
|
136
134
|
method: 'POST',
|
|
137
135
|
});
|
|
138
|
-
if (
|
|
136
|
+
if (tokenResponse.status !== 200) {
|
|
139
137
|
res.setHeader('Content-Type', 'text/html');
|
|
140
138
|
res.send(exports.HTML_ERROR_MSG);
|
|
139
|
+
return;
|
|
141
140
|
}
|
|
142
|
-
const response = await
|
|
141
|
+
const response = await tokenResponse.json();
|
|
143
142
|
this.oauth2Response = {
|
|
144
143
|
accessToken: response.access_token,
|
|
145
144
|
refreshToken: response.refresh_token,
|
|
@@ -148,8 +147,10 @@ class OAuth2Service {
|
|
|
148
147
|
res.send(exports.HTML_SUCCESS_MSG);
|
|
149
148
|
}
|
|
150
149
|
catch (error) {
|
|
151
|
-
res.
|
|
152
|
-
|
|
150
|
+
if (!res.headersSent) {
|
|
151
|
+
res.setHeader('Content-Type', 'text/html');
|
|
152
|
+
res.send(exports.HTML_ERROR_MSG);
|
|
153
|
+
}
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
/**
|
|
@@ -194,25 +195,23 @@ class OAuth2Service {
|
|
|
194
195
|
if (this.clientSecret) {
|
|
195
196
|
bodyData.client_secret = this.clientSecret;
|
|
196
197
|
}
|
|
197
|
-
const fetchOptions = {
|
|
198
|
-
headers: {
|
|
199
|
-
'Content-Type': this.requestContentType,
|
|
200
|
-
...(this.refreshRequestParameters?.header ?? {}),
|
|
201
|
-
},
|
|
202
|
-
body: this.encodeBody(bodyData, this.requestContentType),
|
|
203
|
-
method: 'POST',
|
|
204
|
-
};
|
|
205
198
|
try {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
199
|
+
const tokenResponse = await fetch(this.tokenUrl, {
|
|
200
|
+
headers: {
|
|
201
|
+
'Content-Type': this.requestContentType,
|
|
202
|
+
...(this.refreshRequestParameters?.header ?? {}),
|
|
203
|
+
},
|
|
204
|
+
body: this.encodeBody(bodyData, this.requestContentType),
|
|
205
|
+
method: 'POST',
|
|
206
|
+
});
|
|
207
|
+
if (tokenResponse.status !== 200) {
|
|
208
|
+
throw new errors_1.FailedToRetrieveAccessTokenError(await tokenResponse.text());
|
|
209
209
|
}
|
|
210
|
-
const response = await
|
|
211
|
-
|
|
210
|
+
const response = await tokenResponse.json();
|
|
211
|
+
return {
|
|
212
212
|
accessToken: response.access_token,
|
|
213
213
|
refreshToken: response.refresh_token,
|
|
214
214
|
};
|
|
215
|
-
return credentialsInfo;
|
|
216
215
|
}
|
|
217
216
|
catch (error) {
|
|
218
217
|
throw new errors_1.FailedToRetrieveAccessTokenError(JSON.stringify(error));
|
|
@@ -13,7 +13,7 @@ describe('OAuth2Helper', () => {
|
|
|
13
13
|
let openSpy;
|
|
14
14
|
let fetchStub;
|
|
15
15
|
describe('constructor', () => {
|
|
16
|
-
it('
|
|
16
|
+
it('throws an error if the request content type is not supported', async () => {
|
|
17
17
|
try {
|
|
18
18
|
new oauth2_1.default({ scopes: [], requestContentType: 'random' });
|
|
19
19
|
}
|
|
@@ -51,18 +51,18 @@ describe('OAuth2Helper', () => {
|
|
|
51
51
|
sinon_1.default.restore();
|
|
52
52
|
});
|
|
53
53
|
describe('authorize', () => {
|
|
54
|
-
it('
|
|
54
|
+
it('opens the authorization URL', async () => {
|
|
55
55
|
await oauth2Helper.authorize();
|
|
56
56
|
sinon_1.default.assert.calledOnce(openSpy);
|
|
57
57
|
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?client_id=your-client-id&scope=scope1+scope2&state=eyJjbGlDYWxsYmFja1VybCI6Ii9vYXV0aDIvY2FsbGJhY2sifQ%3D%3D&response_type=code&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli');
|
|
58
58
|
});
|
|
59
|
-
it('
|
|
59
|
+
it('maintains the pre-existing query parameters in authorization URL', async () => {
|
|
60
60
|
oauth2Helper['providerAuthorizationUrl'] = 'https://provider.com/oauth/authorize?query1=value1&query2=value2';
|
|
61
61
|
await oauth2Helper.authorize();
|
|
62
62
|
sinon_1.default.assert.calledOnce(openSpy);
|
|
63
63
|
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?query1=value1&query2=value2&client_id=your-client-id&scope=scope1+scope2&state=eyJjbGlDYWxsYmFja1VybCI6Ii9vYXV0aDIvY2FsbGJhY2sifQ%3D%3D&response_type=code&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli');
|
|
64
64
|
});
|
|
65
|
-
it('
|
|
65
|
+
it('supports dynamic params', async () => {
|
|
66
66
|
oauth2Helper['providerAuthorizationUrl'] = 'https://pro-{+foo}-der.com/oauth/authorize?query1={+bar}';
|
|
67
67
|
await oauth2Helper.authorize();
|
|
68
68
|
sinon_1.default.assert.calledOnce(openSpy);
|
|
@@ -70,7 +70,7 @@ describe('OAuth2Helper', () => {
|
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
72
|
describe('handleCallback', () => {
|
|
73
|
-
it('
|
|
73
|
+
it('retrieves the access token and return it in the response', async () => {
|
|
74
74
|
const code = 'test-code';
|
|
75
75
|
const req = { query: { code } };
|
|
76
76
|
const res = { send: sinon_1.default.stub(), setHeader: sinon_1.default.stub() };
|
|
@@ -87,7 +87,7 @@ describe('OAuth2Helper', () => {
|
|
|
87
87
|
sinon_1.default.assert.calledOnce(res.send);
|
|
88
88
|
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_SUCCESS_MSG);
|
|
89
89
|
});
|
|
90
|
-
it('
|
|
90
|
+
it('handles dynamic variables with values from provider response and credential payload', async () => {
|
|
91
91
|
oauth2Helper['tokenUrl'] = 'https://{+foo}.com/oauth/token?specialParam={+authorizationResponse.responseValue}';
|
|
92
92
|
const code = 'test-code';
|
|
93
93
|
const req = { query: { code, responseValue: 'DynamicValue' } };
|
|
@@ -105,7 +105,7 @@ describe('OAuth2Helper', () => {
|
|
|
105
105
|
sinon_1.default.assert.calledOnce(res.send);
|
|
106
106
|
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_SUCCESS_MSG);
|
|
107
107
|
});
|
|
108
|
-
it('
|
|
108
|
+
it('handles errors - fetch exception', async () => {
|
|
109
109
|
const code = 'test-code';
|
|
110
110
|
const req = { query: { code } };
|
|
111
111
|
const res = { send: sinon_1.default.stub(), setHeader: sinon_1.default.stub() };
|
|
@@ -114,9 +114,18 @@ describe('OAuth2Helper', () => {
|
|
|
114
114
|
sinon_1.default.assert.calledOnce(res.send);
|
|
115
115
|
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_ERROR_MSG);
|
|
116
116
|
});
|
|
117
|
+
it('handles errors - response', async () => {
|
|
118
|
+
const code = 'test-code';
|
|
119
|
+
const req = { query: { code } };
|
|
120
|
+
const res = { send: sinon_1.default.stub(), setHeader: sinon_1.default.stub() };
|
|
121
|
+
fetchStub.resolves({ status: 500 });
|
|
122
|
+
await oauth2Helper['handleCallback'](req, res);
|
|
123
|
+
sinon_1.default.assert.calledOnce(res.send);
|
|
124
|
+
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_ERROR_MSG);
|
|
125
|
+
});
|
|
117
126
|
});
|
|
118
127
|
describe('updateToken', () => {
|
|
119
|
-
it('
|
|
128
|
+
it('retrieves the access token when a refresh token is available', async () => {
|
|
120
129
|
const refreshToken = 'test-refresh-token';
|
|
121
130
|
const accessToken = 'test-access-token';
|
|
122
131
|
const fetchResponse = { access_token: accessToken, refresh_token: refreshToken };
|
|
@@ -125,14 +134,14 @@ describe('OAuth2Helper', () => {
|
|
|
125
134
|
strict_1.default.deepEqual(result, { accessToken, refreshToken });
|
|
126
135
|
sinon_1.default.assert.calledOnce(fetchStub);
|
|
127
136
|
});
|
|
128
|
-
it('
|
|
137
|
+
it('throws an error when failing to refresh the access token', async () => {
|
|
129
138
|
const refreshToken = 'test-refresh-token';
|
|
130
139
|
fetchStub.rejects(new Error('Failed to retrieve access token'));
|
|
131
140
|
const response = oauth2Helper.updateToken(refreshToken);
|
|
132
141
|
await strict_1.default.rejects(response, errors_1.FailedToRetrieveAccessTokenError);
|
|
133
142
|
sinon_1.default.assert.calledOnce(fetchStub);
|
|
134
143
|
});
|
|
135
|
-
it("
|
|
144
|
+
it("throws an error if we don't support the request content type", async () => {
|
|
136
145
|
const refreshToken = 'test-refresh-token';
|
|
137
146
|
oauth2Helper['requestContentType'] = 'random';
|
|
138
147
|
const response = oauth2Helper.updateToken(refreshToken);
|
|
@@ -140,14 +149,14 @@ describe('OAuth2Helper', () => {
|
|
|
140
149
|
});
|
|
141
150
|
});
|
|
142
151
|
describe('encodeBody', () => {
|
|
143
|
-
it('
|
|
152
|
+
it('encodes body data as URL-encoded when content type is URL_ENCODED', () => {
|
|
144
153
|
const bodyData = { key1: 'value1', key2: 'value2' };
|
|
145
154
|
const contentType = configurationTypes_1.RequestContentType.URL_ENCODED;
|
|
146
155
|
const expectedEncodedBody = 'key1=value1&key2=value2';
|
|
147
156
|
const encodedBody = oauth2Helper['encodeBody'](bodyData, contentType);
|
|
148
157
|
strict_1.default.equal(encodedBody, expectedEncodedBody);
|
|
149
158
|
});
|
|
150
|
-
it('
|
|
159
|
+
it('encodes body data as JSON when content type is JSON', () => {
|
|
151
160
|
const bodyData = { key1: 'value1', key2: 'value2' };
|
|
152
161
|
const contentType = configurationTypes_1.RequestContentType.JSON;
|
|
153
162
|
const expectedEncodedBody = JSON.stringify(bodyData);
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.60.
|
|
2
|
+
"version": "0.60.3",
|
|
3
3
|
"commands": {
|
|
4
4
|
"activity": {
|
|
5
5
|
"id": "activity",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"pluginAlias": "@unito/integration-cli",
|
|
10
10
|
"pluginType": "core",
|
|
11
11
|
"aliases": [],
|
|
12
|
+
"hiddenAliases": [],
|
|
12
13
|
"examples": [
|
|
13
14
|
"<%= config.bin %> <%= command.id %>"
|
|
14
15
|
],
|
|
@@ -52,6 +53,7 @@
|
|
|
52
53
|
"pluginAlias": "@unito/integration-cli",
|
|
53
54
|
"pluginType": "core",
|
|
54
55
|
"aliases": [],
|
|
56
|
+
"hiddenAliases": [],
|
|
55
57
|
"examples": [
|
|
56
58
|
"<%= config.bin %> <%= command.id %>"
|
|
57
59
|
],
|
|
@@ -146,6 +148,7 @@
|
|
|
146
148
|
"pluginAlias": "@unito/integration-cli",
|
|
147
149
|
"pluginType": "core",
|
|
148
150
|
"aliases": [],
|
|
151
|
+
"hiddenAliases": [],
|
|
149
152
|
"examples": [
|
|
150
153
|
"<%= config.bin %> <%= command.id %>"
|
|
151
154
|
],
|
|
@@ -179,6 +182,7 @@
|
|
|
179
182
|
"pluginAlias": "@unito/integration-cli",
|
|
180
183
|
"pluginType": "core",
|
|
181
184
|
"aliases": [],
|
|
185
|
+
"hiddenAliases": [],
|
|
182
186
|
"examples": [
|
|
183
187
|
"<%= config.bin %> <%= command.id %>",
|
|
184
188
|
"<%= config.bin %> <%= command.id %> --name pokemon"
|
|
@@ -202,6 +206,7 @@
|
|
|
202
206
|
"pluginAlias": "@unito/integration-cli",
|
|
203
207
|
"pluginType": "core",
|
|
204
208
|
"aliases": [],
|
|
209
|
+
"hiddenAliases": [],
|
|
205
210
|
"examples": [
|
|
206
211
|
"<%= config.bin %> <%= command.id %>"
|
|
207
212
|
],
|
|
@@ -229,6 +234,7 @@
|
|
|
229
234
|
"pluginAlias": "@unito/integration-cli",
|
|
230
235
|
"pluginType": "core",
|
|
231
236
|
"aliases": [],
|
|
237
|
+
"hiddenAliases": [],
|
|
232
238
|
"examples": [
|
|
233
239
|
"<%= config.bin %> <%= command.id %>"
|
|
234
240
|
],
|
|
@@ -257,6 +263,7 @@
|
|
|
257
263
|
"pluginAlias": "@unito/integration-cli",
|
|
258
264
|
"pluginType": "core",
|
|
259
265
|
"aliases": [],
|
|
266
|
+
"hiddenAliases": [],
|
|
260
267
|
"examples": [
|
|
261
268
|
"<%= config.bin %> <%= command.id %>",
|
|
262
269
|
"<%= config.bin %> <%= command.id %> --reauth --test-account=compliance"
|
|
@@ -310,6 +317,7 @@
|
|
|
310
317
|
"pluginAlias": "@unito/integration-cli",
|
|
311
318
|
"pluginType": "core",
|
|
312
319
|
"aliases": [],
|
|
320
|
+
"hiddenAliases": [],
|
|
313
321
|
"examples": [
|
|
314
322
|
"<%= config.bin %> <%= command.id %>"
|
|
315
323
|
],
|
|
@@ -382,6 +390,7 @@
|
|
|
382
390
|
"pluginAlias": "@unito/integration-cli",
|
|
383
391
|
"pluginType": "core",
|
|
384
392
|
"aliases": [],
|
|
393
|
+
"hiddenAliases": [],
|
|
385
394
|
"examples": [
|
|
386
395
|
"<%= config.bin %> <%= command.id %>"
|
|
387
396
|
],
|
|
@@ -513,6 +522,7 @@
|
|
|
513
522
|
"pluginAlias": "@unito/integration-cli",
|
|
514
523
|
"pluginType": "core",
|
|
515
524
|
"aliases": [],
|
|
525
|
+
"hiddenAliases": [],
|
|
516
526
|
"examples": [
|
|
517
527
|
"<%= config.bin %> <%= command.id %>"
|
|
518
528
|
],
|