@unito/integration-cli 0.57.2 → 0.58.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/dist/.eslintrc.d.ts +6 -0
- package/dist/.eslintrc.js +9 -2
- package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/src/index.ts +1 -3
- package/dist/src/commands/dev.d.ts +4 -0
- package/dist/src/commands/dev.js +40 -4
- package/dist/src/commands/init.js +2 -2
- package/dist/src/commands/login.js +1 -1
- package/dist/src/commands/oauth2.js +15 -10
- package/dist/src/commands/test.js +28 -6
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.js +35 -18
- package/dist/src/resources/configuration.js +0 -2
- package/dist/src/resources/credentials.d.ts +3 -0
- package/dist/src/resources/credentials.js +26 -0
- package/dist/src/resources/integrations.d.ts +1 -0
- package/dist/src/resources/integrations.js +37 -2
- package/dist/src/resources/oauth2Helper.d.ts +4 -0
- package/dist/src/resources/oauth2Helper.js +30 -0
- package/dist/src/services/integrationsPlatform.d.ts +1 -0
- package/dist/src/services/integrationsPlatform.js +5 -1
- package/dist/src/services/oauth2Helper.d.ts +77 -3
- package/dist/src/services/oauth2Helper.js +228 -29
- package/dist/test/commands/dev.test.js +21 -0
- package/dist/test/commands/init.test.js +4 -4
- package/dist/test/commands/login.test.js +1 -0
- package/dist/test/commands/oauth2.test.js +2 -1
- package/dist/test/commands/publish.test.js +1 -0
- package/dist/test/commands/test.test.js +22 -0
- package/dist/test/commands/upgrade.test.js +1 -0
- package/dist/test/oauth2Helper/oauth2Helper.test.js +111 -127
- package/dist/test/resources/integrations.test.js +37 -0
- package/dist/test/{services → resources}/oauth2Helper.test.js +12 -42
- package/oclif.manifest.json +64 -9
- package/package.json +8 -8
- package/dist/integrationGenerator/errors.d.ts +0 -2
- package/dist/integrationGenerator/errors.js +0 -6
- package/dist/integrationGenerator/index.d.ts +0 -2
- package/dist/integrationGenerator/index.js +0 -5
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.dockerignore +0 -3
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.eslintrc.js +0 -74
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.nvmrc +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierignore +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.prettierrc +0 -7
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/.unito.json +0 -1
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/Dockerfile +0 -38
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/README.md +0 -21
- package/dist/integrationGenerator/integrationBoilerplate/integrationBoilerplate/src/index.ts +0 -94
- package/dist/integrationGenerator/integrationBoilerplate/package.json +0 -43
- package/dist/integrationGenerator/integrationBoilerplate/src/logger.ts +0 -55
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/additionalLoggingContext.ts +0 -22
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/correlationId.ts +0 -13
- package/dist/integrationGenerator/integrationBoilerplate/src/middlewares/credentials.ts +0 -38
- package/dist/integrationGenerator/integrationBoilerplate/src/request.ts +0 -59
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/index.ts +0 -11
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/me.ts +0 -15
- package/dist/integrationGenerator/integrationBoilerplate/src/routes/root.ts +0 -12
- package/dist/integrationGenerator/integrationBoilerplate/tsconfig.json +0 -37
- package/dist/integrationGenerator/src/index.d.ts +0 -1
- package/dist/integrationGenerator/src/index.js +0 -5
- package/dist/integrationGenerator/src/resources/index.d.ts +0 -1
- package/dist/integrationGenerator/src/resources/index.js +0 -5
- package/dist/integrationGenerator/src/resources/integration.d.ts +0 -9
- package/dist/integrationGenerator/src/resources/integration.js +0 -60
- package/dist/integrationGenerator/test/resources/integration.test.js +0 -50
- package/dist/src/oauth2Helper/oauth2Helper.d.ts +0 -65
- package/dist/src/oauth2Helper/oauth2Helper.js +0 -255
- package/dist/src/oauth2Helper/types.d.ts +0 -22
- package/dist/src/oauth2Helper/types.js +0 -2
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.dockerignore +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.eslintrc.js +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.nvmrc +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.prettierignore +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.prettierrc +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/.unito.json +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/Dockerfile +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate → boilerplate}/README.md +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/package.json +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/logger.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/additionalLoggingContext.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/correlationId.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/middlewares/credentials.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/request.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/index.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/me.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/src/routes/root.ts +0 -0
- /package/dist/{integrationGenerator/integrationBoilerplate/integrationBoilerplate → boilerplate}/tsconfig.json +0 -0
- /package/dist/{integrationGenerator/test/resources/integration.test.d.ts → test/resources/integrations.test.d.ts} +0 -0
- /package/dist/test/{services → resources}/oauth2Helper.test.d.ts +0 -0
|
@@ -4,147 +4,131 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
const strict_1 = tslib_1.__importDefault(require("node:assert/strict"));
|
|
6
6
|
const sinon_1 = tslib_1.__importDefault(require("sinon"));
|
|
7
|
-
const oauth2Helper_1 = tslib_1.__importStar(require("../../src/
|
|
7
|
+
const oauth2Helper_1 = tslib_1.__importStar(require("../../src/services/oauth2Helper")), oauth2Namespace = oauth2Helper_1;
|
|
8
8
|
const configurationTypes_1 = require("../../src/configurationTypes");
|
|
9
9
|
const errors_1 = require("../../src/errors");
|
|
10
10
|
describe('OAuth2Helper', () => {
|
|
11
11
|
let oauth2Helper;
|
|
12
12
|
let openSpy;
|
|
13
13
|
let fetchStub;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
requestContentType: configurationTypes_1.RequestContentType.URL_ENCODED,
|
|
23
|
-
responseContentType: configurationTypes_1.RequestContentType.JSON,
|
|
24
|
-
tokenRequestParameters: {
|
|
25
|
-
header: {
|
|
26
|
-
Authorization: 'custom',
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
oauth2Helper = new oauth2Helper_1.default(authorizationInfo);
|
|
31
|
-
fetchStub = sinon_1.default.stub().resolves({ json: sinon_1.default.stub().resolves({}), status: 200 });
|
|
32
|
-
sinon_1.default.stub(oauth2Helper, 'startServer').resolves('http://localhost:5050');
|
|
33
|
-
sinon_1.default.stub(oauth2Helper, 'stopServer');
|
|
34
|
-
sinon_1.default.replace(global, 'fetch', fetchStub);
|
|
35
|
-
sinon_1.default.replace(oauth2Namespace, 'open', { open: (_url) => undefined });
|
|
36
|
-
openSpy = sinon_1.default.spy(oauth2Namespace.open, 'open');
|
|
37
|
-
});
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
sinon_1.default.restore();
|
|
40
|
-
});
|
|
41
|
-
describe('handleAuthorize', () => {
|
|
42
|
-
it('should open the authorization URL', async () => {
|
|
43
|
-
const req = {};
|
|
44
|
-
const res = { sendStatus: sinon_1.default.stub() };
|
|
45
|
-
await oauth2Helper['handleAuthorize'](req, res);
|
|
46
|
-
sinon_1.default.assert.calledOnce(openSpy);
|
|
47
|
-
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?client_id=your-client-id&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli&state=eyJjbGlDYWxsYmFja1VybCI6Ii9jcmVkZW50aWFscy9uZXcvb2F1dGgyL2NhbGxiYWNrIn0%3D&scope=scope1+scope2&response_type=code');
|
|
48
|
-
sinon_1.default.assert.calledOnce(res.sendStatus);
|
|
49
|
-
});
|
|
50
|
-
it('should maintain the pre-existing query parameters in authorization URL', async () => {
|
|
51
|
-
oauth2Helper['providerAuthorizationUrl'] = 'https://provider.com/oauth/authorize?query1=value1&query2=value2';
|
|
52
|
-
const req = {};
|
|
53
|
-
const res = { sendStatus: sinon_1.default.stub() };
|
|
54
|
-
await oauth2Helper['handleAuthorize'](req, res);
|
|
55
|
-
sinon_1.default.assert.calledOnce(openSpy);
|
|
56
|
-
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?query1=value1&query2=value2&client_id=your-client-id&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli&state=eyJjbGlDYWxsYmFja1VybCI6Ii9jcmVkZW50aWFscy9uZXcvb2F1dGgyL2NhbGxiYWNrIn0%3D&scope=scope1+scope2&response_type=code');
|
|
57
|
-
(0, strict_1.default)(openSpy.firstCall.args[0].includes('query1=value1'));
|
|
58
|
-
(0, strict_1.default)(openSpy.firstCall.args[0].includes('query2=value2'));
|
|
59
|
-
sinon_1.default.assert.calledOnce(res.sendStatus);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('handleCallback', () => {
|
|
63
|
-
it('should store the authorization code', async () => {
|
|
64
|
-
const code = 'test-code';
|
|
65
|
-
const req = { body: { query: { code } } };
|
|
66
|
-
const res = { sendStatus: sinon_1.default.stub() };
|
|
67
|
-
await oauth2Helper['handleCallback'](req, res);
|
|
68
|
-
strict_1.default.equal(oauth2Helper['code'], code);
|
|
69
|
-
sinon_1.default.assert.calledOnce(res.sendStatus);
|
|
14
|
+
describe('constructor', () => {
|
|
15
|
+
it('should throw an error if the request content type is not supported', async () => {
|
|
16
|
+
try {
|
|
17
|
+
new oauth2Helper_1.default({ scopes: [], requestContentType: 'random' });
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
(0, strict_1.default)(err instanceof errors_1.UnsupportedContentTypeError);
|
|
21
|
+
}
|
|
70
22
|
});
|
|
71
23
|
});
|
|
72
|
-
describe('
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
24
|
+
describe('endpoints', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
const authorizationInfo = {
|
|
27
|
+
clientId: 'your-client-id',
|
|
28
|
+
clientSecret: 'your-client-secret',
|
|
29
|
+
authorizationUrl: 'https://provider.com/oauth/authorize',
|
|
30
|
+
scopes: [{ name: 'scope1' }, { name: 'scope2' }],
|
|
31
|
+
tokenUrl: 'https://provider.com/oauth/token',
|
|
32
|
+
grantType: configurationTypes_1.GrantType.AUTHORIZATION_CODE,
|
|
33
|
+
requestContentType: configurationTypes_1.RequestContentType.URL_ENCODED,
|
|
34
|
+
responseContentType: configurationTypes_1.RequestContentType.JSON,
|
|
35
|
+
tokenRequestParameters: {
|
|
36
|
+
header: {
|
|
37
|
+
Authorization: 'custom',
|
|
38
|
+
},
|
|
82
39
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
86
|
-
sinon_1.default.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
fetchStub.rejects(new Error('Failed to retrieve access token'));
|
|
92
|
-
await oauth2Helper['handleToken'](req, res);
|
|
93
|
-
sinon_1.default.assert.calledWith(res.status, 500);
|
|
94
|
-
});
|
|
95
|
-
it('should return an error 400 if the request content type is not supported', async () => {
|
|
96
|
-
const req = { query: {} };
|
|
97
|
-
const res = { status: sinon_1.default.stub().returns({ json: sinon_1.default.stub() }) };
|
|
98
|
-
oauth2Helper['requestContentType'] = 'random';
|
|
99
|
-
await oauth2Helper['handleToken'](req, res);
|
|
100
|
-
sinon_1.default.assert.calledWith(res.status, 400);
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
describe('updateToken', () => {
|
|
104
|
-
it('should throw an error when no refresh token is available', async () => {
|
|
105
|
-
const response = oauth2Helper.updateToken();
|
|
106
|
-
await strict_1.default.rejects(response, errors_1.NoRefreshTokenError);
|
|
40
|
+
};
|
|
41
|
+
oauth2Helper = new oauth2Helper_1.default(authorizationInfo);
|
|
42
|
+
fetchStub = sinon_1.default.stub().resolves({ json: sinon_1.default.stub().resolves({}), status: 200 });
|
|
43
|
+
sinon_1.default.stub(oauth2Helper, 'startServer').resolves('http://localhost:5050');
|
|
44
|
+
sinon_1.default.stub(oauth2Helper, 'stopServer');
|
|
45
|
+
sinon_1.default.replace(global, 'fetch', fetchStub);
|
|
46
|
+
sinon_1.default.replace(oauth2Namespace, 'open', { open: (_url) => undefined });
|
|
47
|
+
openSpy = sinon_1.default.stub(oauth2Namespace.open, 'open');
|
|
107
48
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
oauth2Helper['refreshToken'] = refreshToken;
|
|
111
|
-
const accessToken = 'test-access-token';
|
|
112
|
-
const fetchResponse = { access_token: accessToken, refresh_token: refreshToken };
|
|
113
|
-
fetchStub.resolves({ status: 200, json: sinon_1.default.stub().resolves(fetchResponse) });
|
|
114
|
-
const result = await oauth2Helper.updateToken();
|
|
115
|
-
strict_1.default.deepEqual(result, { accessToken, refreshToken });
|
|
116
|
-
sinon_1.default.assert.calledOnce(fetchStub);
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
sinon_1.default.restore();
|
|
117
51
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
52
|
+
describe('handleAuthorize', () => {
|
|
53
|
+
it('should open the authorization URL', async () => {
|
|
54
|
+
await oauth2Helper.authorize();
|
|
55
|
+
sinon_1.default.assert.calledOnce(openSpy);
|
|
56
|
+
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?client_id=your-client-id&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli&state=eyJjbGlDYWxsYmFja1VybCI6Ii9vYXV0aDIvY2FsbGJhY2sifQ%3D%3D&scope=scope1+scope2&response_type=code');
|
|
57
|
+
});
|
|
58
|
+
it('should maintain the pre-existing query parameters in authorization URL', async () => {
|
|
59
|
+
oauth2Helper['providerAuthorizationUrl'] = 'https://provider.com/oauth/authorize?query1=value1&query2=value2';
|
|
60
|
+
await oauth2Helper.authorize();
|
|
61
|
+
sinon_1.default.assert.calledOnce(openSpy);
|
|
62
|
+
sinon_1.default.assert.calledWith(openSpy, 'https://provider.com/oauth/authorize?query1=value1&query2=value2&client_id=your-client-id&redirect_uri=https%3A%2F%2Fintegrations-platform.unito.io%2Fcredentials%2Fnew%2Foauth2%2Fcallback-cli&state=eyJjbGlDYWxsYmFja1VybCI6Ii9vYXV0aDIvY2FsbGJhY2sifQ%3D%3D&scope=scope1+scope2&response_type=code');
|
|
63
|
+
});
|
|
125
64
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
65
|
+
describe('handleCallback', () => {
|
|
66
|
+
it('should retrieve the access token and return it in the response', async () => {
|
|
67
|
+
const code = 'test-code';
|
|
68
|
+
const req = { query: { code } };
|
|
69
|
+
const res = { send: sinon_1.default.stub(), setHeader: sinon_1.default.stub() };
|
|
70
|
+
await oauth2Helper['handleCallback'](req, res);
|
|
71
|
+
sinon_1.default.assert.calledOnce(fetchStub);
|
|
72
|
+
sinon_1.default.assert.calledWith(fetchStub, 'https://provider.com/oauth/token', {
|
|
73
|
+
headers: {
|
|
74
|
+
'Content-type': 'application/x-www-form-urlencoded',
|
|
75
|
+
Authorization: 'custom',
|
|
76
|
+
},
|
|
77
|
+
body: sinon_1.default.match.string,
|
|
78
|
+
method: 'POST',
|
|
79
|
+
});
|
|
80
|
+
sinon_1.default.assert.calledOnce(res.send);
|
|
81
|
+
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_SUCCESS_MSG);
|
|
82
|
+
});
|
|
83
|
+
it('should handle errors and return 500 status', async () => {
|
|
84
|
+
const code = 'test-code';
|
|
85
|
+
const req = { query: { code } };
|
|
86
|
+
const res = { send: sinon_1.default.stub(), setHeader: sinon_1.default.stub() };
|
|
87
|
+
fetchStub.rejects(new Error('Failed to retrieve access token'));
|
|
88
|
+
await oauth2Helper['handleCallback'](req, res);
|
|
89
|
+
sinon_1.default.assert.calledOnce(res.send);
|
|
90
|
+
sinon_1.default.assert.calledWith(res.send, oauth2Namespace.HTML_ERROR_MSG);
|
|
91
|
+
});
|
|
132
92
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
93
|
+
describe('updateToken', () => {
|
|
94
|
+
it('should retrieve the access token when a refresh token is available', async () => {
|
|
95
|
+
const refreshToken = 'test-refresh-token';
|
|
96
|
+
const accessToken = 'test-access-token';
|
|
97
|
+
const fetchResponse = { access_token: accessToken, refresh_token: refreshToken };
|
|
98
|
+
fetchStub.resolves({ status: 200, json: sinon_1.default.stub().resolves(fetchResponse) });
|
|
99
|
+
const result = await oauth2Helper.updateToken(refreshToken);
|
|
100
|
+
strict_1.default.deepEqual(result, { accessToken, refreshToken });
|
|
101
|
+
sinon_1.default.assert.calledOnce(fetchStub);
|
|
102
|
+
});
|
|
103
|
+
it('should throw an error when failing to refresh the access token', async () => {
|
|
104
|
+
const refreshToken = 'test-refresh-token';
|
|
105
|
+
fetchStub.rejects(new Error('Failed to retrieve access token'));
|
|
106
|
+
const response = oauth2Helper.updateToken(refreshToken);
|
|
107
|
+
await strict_1.default.rejects(response, errors_1.FailedToRetrieveAccessTokenError);
|
|
108
|
+
sinon_1.default.assert.calledOnce(fetchStub);
|
|
109
|
+
});
|
|
110
|
+
it("should throw an error if we don't support the request content type", async () => {
|
|
111
|
+
const refreshToken = 'test-refresh-token';
|
|
112
|
+
oauth2Helper['requestContentType'] = 'random';
|
|
113
|
+
const response = oauth2Helper.updateToken(refreshToken);
|
|
114
|
+
await strict_1.default.rejects(response, errors_1.InvalidRequestContentTypeError);
|
|
115
|
+
});
|
|
141
116
|
});
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
117
|
+
describe('encodeBody', () => {
|
|
118
|
+
it('should encode body data as URL-encoded when content type is URL_ENCODED', () => {
|
|
119
|
+
const bodyData = { key1: 'value1', key2: 'value2' };
|
|
120
|
+
const contentType = configurationTypes_1.RequestContentType.URL_ENCODED;
|
|
121
|
+
const expectedEncodedBody = 'key1=value1&key2=value2';
|
|
122
|
+
const encodedBody = oauth2Helper['encodeBody'](bodyData, contentType);
|
|
123
|
+
strict_1.default.equal(encodedBody, expectedEncodedBody);
|
|
124
|
+
});
|
|
125
|
+
it('should encode body data as JSON when content type is JSON', () => {
|
|
126
|
+
const bodyData = { key1: 'value1', key2: 'value2' };
|
|
127
|
+
const contentType = configurationTypes_1.RequestContentType.JSON;
|
|
128
|
+
const expectedEncodedBody = JSON.stringify(bodyData);
|
|
129
|
+
const encodedBody = oauth2Helper['encodeBody'](bodyData, contentType);
|
|
130
|
+
strict_1.default.equal(encodedBody, expectedEncodedBody);
|
|
131
|
+
});
|
|
148
132
|
});
|
|
149
133
|
});
|
|
150
134
|
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const strict_1 = tslib_1.__importDefault(require("node:assert/strict"));
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const tmp_1 = tslib_1.__importDefault(require("tmp"));
|
|
7
|
+
const fs_1 = tslib_1.__importDefault(require("fs"));
|
|
8
|
+
const Integration = tslib_1.__importStar(require("../../src/resources/integrations"));
|
|
9
|
+
const errors_1 = require("../../src/errors");
|
|
10
|
+
describe('integrations', () => {
|
|
11
|
+
let tempDir;
|
|
12
|
+
const boilerplatePath = path.join(__dirname, '../../boilerplate');
|
|
13
|
+
describe('copyBoilerplate', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tempDir = tmp_1.default.dirSync().name;
|
|
16
|
+
});
|
|
17
|
+
it('copy files', async () => {
|
|
18
|
+
const result = await Integration.copyBoilerplate('foo', tempDir);
|
|
19
|
+
strict_1.default.equal(result, path.join(tempDir, 'foo'));
|
|
20
|
+
(0, strict_1.default)(fs_1.default.existsSync(result));
|
|
21
|
+
const expectedFiles = await fs_1.default.promises.readdir(boilerplatePath);
|
|
22
|
+
expectedFiles.push('.npmrc');
|
|
23
|
+
const createdFiles = await fs_1.default.promises.readdir(result);
|
|
24
|
+
strict_1.default.deepEqual(createdFiles, expectedFiles.sort());
|
|
25
|
+
createdFiles.forEach(createdField => (0, strict_1.default)(expectedFiles.includes(createdField)));
|
|
26
|
+
});
|
|
27
|
+
it('already exists', async () => {
|
|
28
|
+
strict_1.default.doesNotThrow(async () => await Integration.copyBoilerplate('foo', tempDir));
|
|
29
|
+
strict_1.default.rejects(Integration.copyBoilerplate('foo', tempDir), errors_1.IntegrationAlreadyExistsError);
|
|
30
|
+
});
|
|
31
|
+
it('generates an .npmrc file', async () => {
|
|
32
|
+
const result = await Integration.copyBoilerplate('foo', tempDir);
|
|
33
|
+
const npmrc = await fs_1.default.promises.readFile(`${result}/.npmrc`, 'utf-8');
|
|
34
|
+
(0, strict_1.default)(npmrc.includes('engine-strict=true'));
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const strict_1 = tslib_1.__importDefault(require("node:assert/strict"));
|
|
5
5
|
const sinon_1 = tslib_1.__importDefault(require("sinon"));
|
|
6
|
-
const oauth2Helper_1 = tslib_1.__importDefault(require("../../src/
|
|
6
|
+
const oauth2Helper_1 = tslib_1.__importDefault(require("../../src/services/oauth2Helper"));
|
|
7
7
|
const configurationTypes_1 = require("../../src/configurationTypes");
|
|
8
|
-
const
|
|
8
|
+
const oauth2HelperResource = tslib_1.__importStar(require("../../src/resources/oauth2Helper"));
|
|
9
9
|
const errors_1 = require("../../src/errors");
|
|
10
10
|
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
11
11
|
describe('OAuth2Helper', () => {
|
|
@@ -23,7 +23,7 @@ describe('OAuth2Helper', () => {
|
|
|
23
23
|
beforeEach(() => {
|
|
24
24
|
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'startServer').resolves('http://localhost:5050');
|
|
25
25
|
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'stopServer');
|
|
26
|
-
sinon_1.default.stub(oauth2Helper_1.default.prototype, '
|
|
26
|
+
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'authorize');
|
|
27
27
|
sinon_1.default.stub(inquirer_1.default, 'prompt');
|
|
28
28
|
});
|
|
29
29
|
afterEach(() => {
|
|
@@ -31,54 +31,24 @@ describe('OAuth2Helper', () => {
|
|
|
31
31
|
});
|
|
32
32
|
describe('performOAuth2Flow', () => {
|
|
33
33
|
it('should perform the oauth flow', async () => {
|
|
34
|
-
fetchStub = sinon_1.default
|
|
35
|
-
.stub()
|
|
36
|
-
.onFirstCall()
|
|
37
|
-
.resolves({ status: 200 })
|
|
38
|
-
.onSecondCall()
|
|
39
|
-
.resolves({ status: 200 })
|
|
40
|
-
.onThirdCall()
|
|
41
|
-
.resolves({
|
|
42
|
-
status: 200,
|
|
43
|
-
json: () => {
|
|
44
|
-
return { accessToken: 'token', refresToken: 'refreshToken' };
|
|
45
|
-
},
|
|
46
|
-
});
|
|
34
|
+
fetchStub = sinon_1.default.stub().onFirstCall().resolves({ status: 200 }).onSecondCall().resolves({ status: 200 });
|
|
47
35
|
sinon_1.default.replace(global, 'fetch', fetchStub);
|
|
48
|
-
|
|
36
|
+
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'callbackIsDone').resolves({ accessToken: 'token' });
|
|
37
|
+
await oauth2HelperResource.performOAuth2Flow(authorizationInfo);
|
|
49
38
|
strict_1.default.equal(fetchStub.getCall(0).args.at(0), 'http://localhost:5050/health');
|
|
50
|
-
strict_1.default.equal(fetchStub.getCall(1).args.at(0), 'http://localhost:5050/credentials/new/oauth2/authorize');
|
|
51
|
-
strict_1.default.equal(fetchStub.getCall(2).args.at(0), 'http://localhost:5050/credentials/new/oauth2/token');
|
|
52
39
|
});
|
|
53
40
|
it('raises a FailedToRetrieveAccessTokenError', async () => {
|
|
54
|
-
fetchStub = sinon_1.default
|
|
55
|
-
.stub()
|
|
56
|
-
.onFirstCall()
|
|
57
|
-
.resolves({ status: 200 })
|
|
58
|
-
.onSecondCall()
|
|
59
|
-
.resolves({ status: 200 })
|
|
60
|
-
.onThirdCall()
|
|
61
|
-
.resolves({ status: 500, text: () => 'error' });
|
|
41
|
+
fetchStub = sinon_1.default.stub().onFirstCall().resolves({ status: 200 }).onSecondCall().resolves({ status: 200 });
|
|
62
42
|
sinon_1.default.replace(global, 'fetch', fetchStub);
|
|
63
|
-
|
|
43
|
+
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'callbackIsDone').resolves({});
|
|
44
|
+
const response = oauth2HelperResource.performOAuth2Flow(authorizationInfo);
|
|
64
45
|
await strict_1.default.rejects(response, errors_1.FailedToRetrieveAccessTokenError);
|
|
65
46
|
});
|
|
66
47
|
it('raises a FailedToRetrieveAccessTokenError when the accessToken is not returned', async () => {
|
|
67
|
-
fetchStub = sinon_1.default
|
|
68
|
-
.stub()
|
|
69
|
-
.onFirstCall()
|
|
70
|
-
.resolves({ status: 200 })
|
|
71
|
-
.onSecondCall()
|
|
72
|
-
.resolves({ status: 200 })
|
|
73
|
-
.onThirdCall()
|
|
74
|
-
.resolves({
|
|
75
|
-
status: 200,
|
|
76
|
-
json: () => {
|
|
77
|
-
return {};
|
|
78
|
-
},
|
|
79
|
-
});
|
|
48
|
+
fetchStub = sinon_1.default.stub().onFirstCall().resolves({ status: 200 }).onSecondCall().resolves({ status: 200 });
|
|
80
49
|
sinon_1.default.replace(global, 'fetch', fetchStub);
|
|
81
|
-
|
|
50
|
+
sinon_1.default.stub(oauth2Helper_1.default.prototype, 'callbackIsDone').resolves({});
|
|
51
|
+
const response = oauth2HelperResource.performOAuth2Flow(authorizationInfo);
|
|
82
52
|
await strict_1.default.rejects(response, errors_1.FailedToRetrieveAccessTokenError);
|
|
83
53
|
});
|
|
84
54
|
});
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.58.0",
|
|
3
3
|
"commands": {
|
|
4
4
|
"activity": {
|
|
5
5
|
"id": "activity",
|
|
@@ -74,6 +74,41 @@
|
|
|
74
74
|
"description": "output more (debug) information",
|
|
75
75
|
"allowNo": false
|
|
76
76
|
},
|
|
77
|
+
"credential-payload": {
|
|
78
|
+
"name": "credential-payload",
|
|
79
|
+
"type": "option",
|
|
80
|
+
"description": "(advanced) credential payload to use.",
|
|
81
|
+
"multiple": false,
|
|
82
|
+
"exclusive": [
|
|
83
|
+
"credential-id"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"credential-id": {
|
|
87
|
+
"name": "credential-id",
|
|
88
|
+
"type": "option",
|
|
89
|
+
"description": "(advanced) credential to use.",
|
|
90
|
+
"multiple": false,
|
|
91
|
+
"exclusive": [
|
|
92
|
+
"credential-payload"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"test-account": {
|
|
96
|
+
"name": "test-account",
|
|
97
|
+
"type": "option",
|
|
98
|
+
"description": "test account to use.",
|
|
99
|
+
"multiple": false,
|
|
100
|
+
"options": [
|
|
101
|
+
"development",
|
|
102
|
+
"compliance"
|
|
103
|
+
],
|
|
104
|
+
"default": "development"
|
|
105
|
+
},
|
|
106
|
+
"read-only": {
|
|
107
|
+
"name": "read-only",
|
|
108
|
+
"type": "boolean",
|
|
109
|
+
"description": "whether or not to only perform read operations",
|
|
110
|
+
"allowNo": true
|
|
111
|
+
},
|
|
77
112
|
"crawlMode": {
|
|
78
113
|
"name": "crawlMode",
|
|
79
114
|
"type": "option",
|
|
@@ -209,15 +244,16 @@
|
|
|
209
244
|
},
|
|
210
245
|
"oauth2": {
|
|
211
246
|
"id": "oauth2",
|
|
212
|
-
"summary": "Perform an OAuth2 workflow
|
|
213
|
-
"description": "The Oauth2 command allows you to perform an OAuth2 workflow
|
|
247
|
+
"summary": "Perform an OAuth2 workflow to either populate or refresh a test account's credentials.",
|
|
248
|
+
"description": "The Oauth2 command allows you to perform an OAuth2 workflow to populate the specified test account's accessToken and refreshToken to be used by the 'test' and 'dev' commands.\n\n If the test-account is already populated, the command will refresh the credentials if the refresh token is available.\n If you want to force a new OAuth2 flow to change the connected account or something else, use the --reauth flag.",
|
|
214
249
|
"strict": true,
|
|
215
250
|
"pluginName": "@unito/integration-cli",
|
|
216
251
|
"pluginAlias": "@unito/integration-cli",
|
|
217
252
|
"pluginType": "core",
|
|
218
253
|
"aliases": [],
|
|
219
254
|
"examples": [
|
|
220
|
-
"<%= config.bin %> <%= command.id %>"
|
|
255
|
+
"<%= config.bin %> <%= command.id %>",
|
|
256
|
+
"<%= config.bin %> <%= command.id %> --reauth --test-account=compliance"
|
|
221
257
|
],
|
|
222
258
|
"flags": {
|
|
223
259
|
"test-account": {
|
|
@@ -235,6 +271,7 @@
|
|
|
235
271
|
"name": "environment",
|
|
236
272
|
"type": "option",
|
|
237
273
|
"description": "the environment of the platform",
|
|
274
|
+
"hidden": true,
|
|
238
275
|
"multiple": false,
|
|
239
276
|
"options": [
|
|
240
277
|
"local",
|
|
@@ -246,7 +283,7 @@
|
|
|
246
283
|
"reauth": {
|
|
247
284
|
"name": "reauth",
|
|
248
285
|
"type": "boolean",
|
|
249
|
-
"description": "triggers a new oauth2 flow to
|
|
286
|
+
"description": "triggers a new oauth2 flow to overwrite the test account's current credentials",
|
|
250
287
|
"allowNo": false
|
|
251
288
|
},
|
|
252
289
|
"config-path": {
|
|
@@ -394,13 +431,25 @@
|
|
|
394
431
|
"credential-payload": {
|
|
395
432
|
"name": "credential-payload",
|
|
396
433
|
"type": "option",
|
|
434
|
+
"description": "(advanced) credential payload to use.",
|
|
435
|
+
"multiple": false,
|
|
436
|
+
"exclusive": [
|
|
437
|
+
"credential-id"
|
|
438
|
+
]
|
|
439
|
+
},
|
|
440
|
+
"credential-id": {
|
|
441
|
+
"name": "credential-id",
|
|
442
|
+
"type": "option",
|
|
397
443
|
"description": "(advanced) credential to use.",
|
|
398
|
-
"multiple": false
|
|
444
|
+
"multiple": false,
|
|
445
|
+
"exclusive": [
|
|
446
|
+
"credential-payload"
|
|
447
|
+
]
|
|
399
448
|
},
|
|
400
449
|
"test-account": {
|
|
401
450
|
"name": "test-account",
|
|
402
451
|
"type": "option",
|
|
403
|
-
"description": "
|
|
452
|
+
"description": "test account to use.",
|
|
404
453
|
"multiple": false,
|
|
405
454
|
"options": [
|
|
406
455
|
"development",
|
|
@@ -408,6 +457,12 @@
|
|
|
408
457
|
],
|
|
409
458
|
"default": "development"
|
|
410
459
|
},
|
|
460
|
+
"read-only": {
|
|
461
|
+
"name": "read-only",
|
|
462
|
+
"type": "boolean",
|
|
463
|
+
"description": "whether or not to only perform read operations",
|
|
464
|
+
"allowNo": true
|
|
465
|
+
},
|
|
411
466
|
"crawlMode": {
|
|
412
467
|
"name": "crawlMode",
|
|
413
468
|
"type": "option",
|
|
@@ -430,7 +485,7 @@
|
|
|
430
485
|
"debug": {
|
|
431
486
|
"name": "debug",
|
|
432
487
|
"type": "boolean",
|
|
433
|
-
"description": "
|
|
488
|
+
"description": "log launch command to console before running it - including decrypted values - for debugging only",
|
|
434
489
|
"hidden": true,
|
|
435
490
|
"allowNo": false
|
|
436
491
|
},
|
|
@@ -438,7 +493,7 @@
|
|
|
438
493
|
"name": "config-path",
|
|
439
494
|
"type": "option",
|
|
440
495
|
"summary": "relative path to a custom \".unito.json\" file",
|
|
441
|
-
"description": "
|
|
496
|
+
"description": "use a custom configuration file instead of the default '.unito.json' or other environment specific\n ones.\n\n If you want to force the CLI to use a specific configuration file, you can use this flag to specify the relative\n path from your integration's root folder (with a leading '/').\n\n Usage: <%= config.bin %> <%= command.id %> --config-path=/myCustomConfig.json",
|
|
442
497
|
"multiple": false
|
|
443
498
|
}
|
|
444
499
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unito/integration-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "Integration CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"integration-cli": "./bin/run"
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"email": "hello@unito.io"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
16
|
+
"node": ">=20.0.0"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
19
|
"prepublishOnly": "npm run lint && npm run test",
|
|
20
20
|
"prepack": "oclif manifest",
|
|
21
21
|
"postpack": "shx rm -f oclif.manifest.json",
|
|
22
22
|
"prepare": "npm run compile",
|
|
23
|
-
"lint": "eslint --fix src test scripts
|
|
24
|
-
"compile": "ts-node scripts/generateTypes.ts > src/configurationTypes.ts && tsc -b && mkdir -p dist/schemas && cp schemas/*.json dist/schemas/ && cp -r
|
|
23
|
+
"lint": "eslint --fix src test scripts --ext .ts && prettier --write src test scripts",
|
|
24
|
+
"compile": "rm -rf dist && ts-node scripts/generateTypes.ts > src/configurationTypes.ts && tsc -b && mkdir -p dist/schemas && cp schemas/*.json dist/schemas/ && cp -r boilerplate ./dist/boilerplate",
|
|
25
25
|
"compile:watch": "tsc --build -w",
|
|
26
|
-
"test": "mocha
|
|
27
|
-
"test:debug": "mocha
|
|
26
|
+
"test": "mocha \"test/**/*.test.ts\"",
|
|
27
|
+
"test:debug": "mocha --inspect-brk \"test/**/*.test.ts\"",
|
|
28
28
|
"ci:test": "c8 npm run test"
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@oclif/core": "3.x",
|
|
39
39
|
"@typescript-eslint/eslint-plugin": "6.x",
|
|
40
40
|
"@typescript-eslint/parser": "6.13.x",
|
|
41
|
-
"@unito/integration-debugger": "0.
|
|
41
|
+
"@unito/integration-debugger": "0.24.0",
|
|
42
42
|
"@unito/integrations-platform-client": "0.46.1",
|
|
43
43
|
"ajv": "8.x",
|
|
44
44
|
"ajv-formats": "2.x",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@types/gradient-string": "1.x",
|
|
63
63
|
"@types/inquirer": "9.x",
|
|
64
64
|
"@types/mocha": "10.x",
|
|
65
|
-
"@types/node": "
|
|
65
|
+
"@types/node": "20.x",
|
|
66
66
|
"@types/openurl": "1.x",
|
|
67
67
|
"@types/tmp": "0.x",
|
|
68
68
|
"c8": "9.x",
|