1 | 'use strict';
|
2 |
|
3 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
4 |
|
5 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
6 |
|
7 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
8 |
|
9 | var http = require('http');
|
10 | var https = require('https');
|
11 | var utils = require('./utils');
|
12 | var Environment = require('./environment');
|
13 | var UserAgent = require('./user-agent');
|
14 | var retry = require('bluebird-retry');
|
15 | var requestPromise = require('request-promise');
|
16 | var PromisePool = require('es6-promise-pool');
|
17 | var regeneratorRuntime = require('regenerator-runtime');
|
18 | var fs = require('fs');
|
19 |
|
20 | require('dotenv').config();
|
21 |
|
22 | var JSON_API_CONTENT_TYPE = 'application/vnd.api+json';
|
23 | var CONCURRENCY = 2;
|
24 |
|
25 | var Resource = function () {
|
26 | function Resource(options) {
|
27 | _classCallCheck(this, Resource);
|
28 |
|
29 | if (!options.resourceUrl) {
|
30 | throw new Error('"resourceUrl" arg is required to create a Resource.');
|
31 | }
|
32 | if (!options.sha && !options.content) {
|
33 | throw new Error('Either "sha" or "content" is required to create a Resource.');
|
34 | }
|
35 | if (/\s/.test(options.resourceUrl)) {
|
36 | throw new Error('"resourceUrl" arg includes whitespace. It needs to be encoded.');
|
37 | }
|
38 | this.resourceUrl = options.resourceUrl;
|
39 | this.content = options.content;
|
40 | this.sha = options.sha || utils.sha256hash(options.content);
|
41 | this.mimetype = options.mimetype;
|
42 | this.isRoot = options.isRoot;
|
43 |
|
44 |
|
45 |
|
46 | this.localPath = options.localPath;
|
47 | }
|
48 |
|
49 | _createClass(Resource, [{
|
50 | key: 'serialize',
|
51 | value: function serialize() {
|
52 | return {
|
53 | type: 'resources',
|
54 | id: this.sha,
|
55 | attributes: {
|
56 | 'resource-url': this.resourceUrl,
|
57 | mimetype: this.mimetype || null,
|
58 | 'is-root': this.isRoot || null
|
59 | }
|
60 | };
|
61 | }
|
62 | }]);
|
63 |
|
64 | return Resource;
|
65 | }();
|
66 |
|
67 | var PercyClient = function () {
|
68 | function PercyClient(options) {
|
69 | _classCallCheck(this, PercyClient);
|
70 |
|
71 | options = options || {};
|
72 | this.token = options.token;
|
73 | this.apiUrl = options.apiUrl || 'https://percy.io/api/v1';
|
74 | this.environment = options.environment || new Environment(process.env);
|
75 | this._httpClient = requestPromise;
|
76 | this._httpModule = this.apiUrl.indexOf('http://') === 0 ? http : https;
|
77 |
|
78 | this._httpAgent = new this._httpModule.Agent({
|
79 | maxSockets: 5,
|
80 | keepAlive: true
|
81 | });
|
82 | this._clientInfo = options.clientInfo;
|
83 | this._environmentInfo = options.environmentInfo;
|
84 | this._sdkClientInfo = null;
|
85 | this._sdkEnvironmentInfo = null;
|
86 | }
|
87 |
|
88 | _createClass(PercyClient, [{
|
89 | key: '_headers',
|
90 | value: function _headers(headers) {
|
91 | return _extends({
|
92 | Authorization: 'Token token=' + this.token,
|
93 | 'User-Agent': new UserAgent(this).toString()
|
94 | }, headers);
|
95 | }
|
96 | }, {
|
97 | key: '_httpGet',
|
98 | value: function _httpGet(uri) {
|
99 | var requestOptions = {
|
100 | method: 'GET',
|
101 | uri: uri,
|
102 | headers: this._headers(),
|
103 | json: true,
|
104 | resolveWithFullResponse: true,
|
105 | agent: this._httpAgent
|
106 | };
|
107 |
|
108 | return retry(this._httpClient, {
|
109 | context: this,
|
110 | args: [uri, requestOptions],
|
111 | interval: 50,
|
112 | max_tries: 5,
|
113 | throw_original: true,
|
114 | predicate: function predicate(err) {
|
115 | return err.statusCode >= 500 && err.statusCode < 600;
|
116 | }
|
117 | });
|
118 | }
|
119 | }, {
|
120 | key: '_httpPost',
|
121 | value: function _httpPost(uri, data) {
|
122 | var requestOptions = {
|
123 | method: 'POST',
|
124 | uri: uri,
|
125 | body: data,
|
126 | headers: this._headers({ 'Content-Type': JSON_API_CONTENT_TYPE }),
|
127 | json: true,
|
128 | resolveWithFullResponse: true,
|
129 | agent: this._httpAgent
|
130 | };
|
131 |
|
132 | return retry(this._httpClient, {
|
133 | context: this,
|
134 | args: [uri, requestOptions],
|
135 | interval: 50,
|
136 | max_tries: 5,
|
137 | throw_original: true,
|
138 | predicate: function predicate(err) {
|
139 | return err.statusCode >= 500 && err.statusCode < 600;
|
140 | }
|
141 | });
|
142 | }
|
143 | }, {
|
144 | key: 'createBuild',
|
145 | value: function createBuild(options) {
|
146 | var parallelNonce = this.environment.parallelNonce;
|
147 | var parallelTotalShards = this.environment.parallelTotalShards;
|
148 |
|
149 |
|
150 | if (!parallelNonce || !parallelTotalShards) {
|
151 | parallelNonce = null;
|
152 | parallelTotalShards = null;
|
153 | }
|
154 |
|
155 | options = options || {};
|
156 |
|
157 | var commitData = options['commitData'] || this.environment.commitData;
|
158 |
|
159 | var data = {
|
160 | data: {
|
161 | type: 'builds',
|
162 | attributes: {
|
163 | branch: commitData.branch,
|
164 | 'target-branch': this.environment.targetBranch,
|
165 | 'target-commit-sha': this.environment.targetCommitSha,
|
166 | 'commit-sha': commitData.sha,
|
167 | 'commit-committed-at': commitData.committedAt,
|
168 | 'commit-author-name': commitData.authorName,
|
169 | 'commit-author-email': commitData.authorEmail,
|
170 | 'commit-committer-name': commitData.committerName,
|
171 | 'commit-committer-email': commitData.committerEmail,
|
172 | 'commit-message': commitData.message,
|
173 | 'pull-request-number': this.environment.pullRequestNumber,
|
174 | 'parallel-nonce': parallelNonce,
|
175 | 'parallel-total-shards': parallelTotalShards,
|
176 | partial: this.environment.partialBuild
|
177 | }
|
178 | }
|
179 | };
|
180 |
|
181 | if (options.resources) {
|
182 | data['data']['relationships'] = {
|
183 | resources: {
|
184 | data: options.resources.map(function (resource) {
|
185 | return resource.serialize();
|
186 | })
|
187 | }
|
188 | };
|
189 | }
|
190 |
|
191 | return this._httpPost(this.apiUrl + '/builds/', data);
|
192 | }
|
193 |
|
194 |
|
195 |
|
196 | }, {
|
197 | key: 'getBuild',
|
198 | value: function getBuild(buildId) {
|
199 | return this._httpGet(this.apiUrl + '/builds/' + buildId);
|
200 | }
|
201 |
|
202 |
|
203 |
|
204 | }, {
|
205 | key: 'getBuilds',
|
206 | value: function getBuilds(project, filter) {
|
207 | filter = filter || {};
|
208 | var queryString = Object.keys(filter).map(function (key) {
|
209 | return 'filter[' + key + ']=' + filter[key];
|
210 | }).join('&');
|
211 |
|
212 | if (queryString.length > 0) {
|
213 | queryString = '?' + queryString;
|
214 | }
|
215 |
|
216 | return this._httpGet(this.apiUrl + '/projects/' + project + '/builds' + queryString);
|
217 | }
|
218 | }, {
|
219 | key: 'makeResource',
|
220 | value: function makeResource(options) {
|
221 | return new Resource(options);
|
222 | }
|
223 |
|
224 |
|
225 |
|
226 | }, {
|
227 | key: 'gatherBuildResources',
|
228 | value: function gatherBuildResources(rootDir, options) {
|
229 | return utils.gatherBuildResources(this, rootDir, options);
|
230 | }
|
231 | }, {
|
232 | key: 'uploadResource',
|
233 | value: function uploadResource(buildId, content) {
|
234 | var sha = utils.sha256hash(content);
|
235 | var data = {
|
236 | data: {
|
237 | type: 'resources',
|
238 | id: sha,
|
239 | attributes: {
|
240 | 'base64-content': utils.base64encode(content)
|
241 | }
|
242 | }
|
243 | };
|
244 |
|
245 | return this._httpPost(this.apiUrl + '/builds/' + buildId + '/resources/', data);
|
246 | }
|
247 | }, {
|
248 | key: 'uploadResources',
|
249 | value: function uploadResources(buildId, resources) {
|
250 | var _marked = regeneratorRuntime.mark(generatePromises);
|
251 |
|
252 | var _this = this;
|
253 | function generatePromises() {
|
254 | var _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, resource, content;
|
255 |
|
256 | return regeneratorRuntime.wrap(function generatePromises$(_context) {
|
257 | while (1) {
|
258 | switch (_context.prev = _context.next) {
|
259 | case 0:
|
260 | _iteratorNormalCompletion = true;
|
261 | _didIteratorError = false;
|
262 | _iteratorError = undefined;
|
263 | _context.prev = 3;
|
264 | _iterator = resources[Symbol.iterator]();
|
265 |
|
266 | case 5:
|
267 | if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
|
268 | _context.next = 13;
|
269 | break;
|
270 | }
|
271 |
|
272 | resource = _step.value;
|
273 | content = resource.localPath ? fs.readFileSync(resource.localPath) : resource.content;
|
274 | _context.next = 10;
|
275 | return _this.uploadResource(buildId, content);
|
276 |
|
277 | case 10:
|
278 | _iteratorNormalCompletion = true;
|
279 | _context.next = 5;
|
280 | break;
|
281 |
|
282 | case 13:
|
283 | _context.next = 19;
|
284 | break;
|
285 |
|
286 | case 15:
|
287 | _context.prev = 15;
|
288 | _context.t0 = _context['catch'](3);
|
289 | _didIteratorError = true;
|
290 | _iteratorError = _context.t0;
|
291 |
|
292 | case 19:
|
293 | _context.prev = 19;
|
294 | _context.prev = 20;
|
295 |
|
296 | if (!_iteratorNormalCompletion && _iterator.return) {
|
297 | _iterator.return();
|
298 | }
|
299 |
|
300 | case 22:
|
301 | _context.prev = 22;
|
302 |
|
303 | if (!_didIteratorError) {
|
304 | _context.next = 25;
|
305 | break;
|
306 | }
|
307 |
|
308 | throw _iteratorError;
|
309 |
|
310 | case 25:
|
311 | return _context.finish(22);
|
312 |
|
313 | case 26:
|
314 | return _context.finish(19);
|
315 |
|
316 | case 27:
|
317 | case 'end':
|
318 | return _context.stop();
|
319 | }
|
320 | }
|
321 | }, _marked, this, [[3, 15, 19, 27], [20,, 22, 26]]);
|
322 | }
|
323 |
|
324 | var pool = new PromisePool(generatePromises(), CONCURRENCY);
|
325 | return pool.start();
|
326 | }
|
327 | }, {
|
328 | key: 'uploadMissingResources',
|
329 | value: function uploadMissingResources(buildId, response, resources) {
|
330 | var missingResourceShas = utils.getMissingResources(response);
|
331 | if (!missingResourceShas.length) {
|
332 | return Promise.resolve();
|
333 | }
|
334 |
|
335 | var resourcesBySha = resources.reduce(function (map, resource) {
|
336 | map[resource.sha] = resource;
|
337 | return map;
|
338 | }, {});
|
339 | var missingResources = missingResourceShas.map(function (resource) {
|
340 | return resourcesBySha[resource.id];
|
341 | });
|
342 |
|
343 | return this.uploadResources(buildId, missingResources);
|
344 | }
|
345 | }, {
|
346 | key: 'createSnapshot',
|
347 | value: function createSnapshot(buildId, resources, options) {
|
348 | options = options || {};
|
349 | resources = resources || [];
|
350 |
|
351 | var data = {
|
352 | data: {
|
353 | type: 'snapshots',
|
354 | attributes: {
|
355 | name: options.name || null,
|
356 | 'enable-javascript': options.enableJavaScript || null,
|
357 | widths: options.widths || null,
|
358 | 'minimum-height': options.minimumHeight || null
|
359 | },
|
360 | relationships: {
|
361 | resources: {
|
362 | data: resources.map(function (resource) {
|
363 | return resource.serialize();
|
364 | })
|
365 | }
|
366 | }
|
367 | }
|
368 | };
|
369 |
|
370 | this._sdkClientInfo = options.clientInfo;
|
371 | this._sdkEnvironmentInfo = options.environmentInfo;
|
372 | return this._httpPost(this.apiUrl + '/builds/' + buildId + '/snapshots/', data);
|
373 | }
|
374 | }, {
|
375 | key: 'finalizeSnapshot',
|
376 | value: function finalizeSnapshot(snapshotId) {
|
377 | return this._httpPost(this.apiUrl + '/snapshots/' + snapshotId + '/finalize', {});
|
378 | }
|
379 | }, {
|
380 | key: 'finalizeBuild',
|
381 | value: function finalizeBuild(buildId, options) {
|
382 | options = options || {};
|
383 | var allShards = options.allShards || false;
|
384 | var query = allShards ? '?all-shards=true' : '';
|
385 | return this._httpPost(this.apiUrl + '/builds/' + buildId + '/finalize' + query, {});
|
386 | }
|
387 | }]);
|
388 |
|
389 | return PercyClient;
|
390 | }();
|
391 |
|
392 | module.exports = PercyClient; |
\ | No newline at end of file |