Skip to content

Commit cbcb04e

Browse files
sakka2catto
authored andcommitted
fix(1030): Add method to call generating build JWT API and replace temporal JWT (#42)
* add temporal JWT * add unit test * remove unnecessay file * delete package-lock.json * fix test * fix test * delete try/catch * fix to return value * fix test * fix test * fix JSDoc * strictSSL true * fix test * delete json
1 parent 00e272a commit cbcb04e

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

index.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
const Joi = require('joi');
55
const dataSchema = require('screwdriver-data-schema');
66
const executorSchema = dataSchema.plugins.executor;
7+
const request = require('requestretry');
8+
const DEFAULT_BUILD_TIMEOUT = 90; // in minutes
79

810
/**
911
* Validate the config using the schema
@@ -121,6 +123,39 @@ class Executor {
121123
stats() {
122124
return {};
123125
}
126+
127+
/**
128+
* Get JWT for build by using temporal JWT via API
129+
* @method exchangeTokenForBuild
130+
* @param {Object} config A configuration object
131+
* @param {String} config.apiUrl Base URL for Screwdriver API
132+
* @param {String} config.buildId Build ID
133+
* @param {String} config.token Temporal JWT
134+
* @param {String} buildTimeout Build timeout value which will be JWT expires time
135+
* @return {Promise}
136+
*/
137+
async exchangeTokenForBuild(config, buildTimeout = DEFAULT_BUILD_TIMEOUT) {
138+
if (isFinite(buildTimeout) === false) {
139+
throw new Error(`Invalid buildTimeout value: ${buildTimeout}`);
140+
}
141+
142+
const options = {
143+
uri: `${config.apiUri}/v4/builds/${config.buildId}/token`,
144+
method: 'POST',
145+
body: { buildTimeout },
146+
headers: { Authorization: `Bearer ${config.token}` },
147+
strictSSL: true,
148+
json: true
149+
};
150+
151+
const response = await request(options);
152+
153+
if (response.statusCode !== 200) {
154+
throw new Error(`Failed to exchange build token: ${JSON.stringify(response.body)}`);
155+
}
156+
157+
return response.body.token;
158+
}
124159
}
125160

126161
module.exports = Executor;

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
"eslint": "^4.19.1",
4242
"eslint-config-screwdriver": "^3.0.0",
4343
"jenkins-mocha": "^4.0.0",
44-
"mockery": "^2.0.0"
44+
"mockery": "^2.0.0",
45+
"sinon": "^2.3.1"
4546
},
4647
"dependencies": {
4748
"joi": "^13.0.0",
49+
"requestretry": "^1.13.0",
4850
"screwdriver-data-schema": "^18.20.0"
4951
}
5052
}

test/index.test.js

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
'use strict';
22

33
const { assert } = require('chai');
4+
const sinon = require('sinon');
45
const mockery = require('mockery');
56
const Joi = require('joi');
7+
const DEFAULT_BUILD_TIMEOUT = 90; // in minutes
68

79
describe('index test', () => {
810
let instance;
911
let schemaMock;
1012
let Executor;
13+
let requestMock;
1114

1215
before(() => {
1316
mockery.enable({
@@ -24,10 +27,14 @@ describe('index test', () => {
2427
stop: Joi.object().required(),
2528
startPeriodic: Joi.object().required(),
2629
stopPeriodic: Joi.object().required(),
27-
status: Joi.object().required()
30+
status: Joi.object().required(),
31+
exchangeTokenForBuild: Joi.object().required()
2832
}
2933
}
3034
};
35+
requestMock = sinon.stub();
36+
mockery.registerMock('requestretry', requestMock);
37+
3138
mockery.registerMock('screwdriver-data-schema', schemaMock);
3239

3340
// eslint-disable-next-line global-require
@@ -151,4 +158,77 @@ describe('index test', () => {
151158
assert.equal(data.buildId, 'a');
152159
});
153160
});
161+
162+
describe('exchangeTokenForBuild', () => {
163+
let postConfig;
164+
let options;
165+
let buildTimeout;
166+
let fakeResponse;
167+
168+
beforeEach(() => {
169+
postConfig = {
170+
buildId: 111,
171+
apiUri: 'https://dummy.com',
172+
token: 'dummyTemporalToken'
173+
};
174+
buildTimeout = 150;
175+
options = {
176+
uri: `${postConfig.apiUri}/v4/builds/${postConfig.buildId}/token`,
177+
method: 'POST',
178+
body: { buildTimeout },
179+
headers: { Authorization: `Bearer ${postConfig.token}` },
180+
strictSSL: true,
181+
json: true
182+
};
183+
fakeResponse = {
184+
statusCode: 200,
185+
body: {
186+
token: 'dummyBuildToken'
187+
}
188+
};
189+
});
190+
191+
it('succeeds to exchange temporal JWT to build JWT', async () => {
192+
requestMock.withArgs(options).resolves(fakeResponse);
193+
194+
await instance.exchangeTokenForBuild(postConfig, buildTimeout).then((buildToken) => {
195+
assert.equal(fakeResponse.body.token, buildToken);
196+
});
197+
});
198+
199+
it('succeeds to exchange temporal JWT to build JWT without buildTimeout args', async () => {
200+
options.body.buildTimeout = DEFAULT_BUILD_TIMEOUT;
201+
requestMock.withArgs(options).resolves(fakeResponse);
202+
203+
await instance.exchangeTokenForBuild(postConfig).then((buildToken) => {
204+
assert.equal(fakeResponse.body.token, buildToken);
205+
});
206+
});
207+
208+
it('returns error if buildTimeout value is invalid', async () => {
209+
buildTimeout = 'aaa';
210+
const returnMessage = `Invalid buildTimeout value: ${buildTimeout}`;
211+
212+
await instance.exchangeTokenForBuild(postConfig, buildTimeout).then(() => {
213+
throw new Error('did not fail');
214+
}, (err) => {
215+
assert.equal(err.message, returnMessage);
216+
});
217+
});
218+
219+
it('returns error if response code is not 200', async () => {
220+
fakeResponse.statusCode = 409;
221+
222+
const returnMessage =
223+
`Failed to exchange build token: ${JSON.stringify(fakeResponse.body)}`;
224+
225+
requestMock.withArgs(options).resolves(fakeResponse);
226+
227+
await instance.exchangeTokenForBuild(postConfig, buildTimeout).then(() => {
228+
throw new Error('did not fail');
229+
}, (err) => {
230+
assert.equal(err.message, returnMessage);
231+
});
232+
});
233+
});
154234
});

0 commit comments

Comments
 (0)